xref: /illumos-gate/usr/src/boot/libsa/zfs/zfsimpl.c (revision 2c45cde9bee327a9e717fab5fbc303abcdd61fbf)
122028508SToomas Soome /*
222028508SToomas Soome  * Copyright (c) 2007 Doug Rabson
322028508SToomas Soome  * All rights reserved.
422028508SToomas Soome  *
522028508SToomas Soome  * Redistribution and use in source and binary forms, with or without
622028508SToomas Soome  * modification, are permitted provided that the following conditions
722028508SToomas Soome  * are met:
822028508SToomas Soome  * 1. Redistributions of source code must retain the above copyright
922028508SToomas Soome  *    notice, this list of conditions and the following disclaimer.
1022028508SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
1122028508SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
1222028508SToomas Soome  *    documentation and/or other materials provided with the distribution.
1322028508SToomas Soome  *
1422028508SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1522028508SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1622028508SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1722028508SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1822028508SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1922028508SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2022028508SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2122028508SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2222028508SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2322028508SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2422028508SToomas Soome  * SUCH DAMAGE.
2522028508SToomas Soome  */
2622028508SToomas Soome 
2722028508SToomas Soome #include <sys/cdefs.h>
2822028508SToomas Soome 
2922028508SToomas Soome /*
3022028508SToomas Soome  *	Stand-alone ZFS file reader.
3122028508SToomas Soome  */
3222028508SToomas Soome 
3322028508SToomas Soome #include <stdbool.h>
3422028508SToomas Soome #include <sys/endian.h>
3522028508SToomas Soome #include <sys/stat.h>
3622028508SToomas Soome #include <sys/stdint.h>
3722028508SToomas Soome #include <sys/list.h>
3822028508SToomas Soome #include <sys/zfs_bootenv.h>
3922028508SToomas Soome #include <inttypes.h>
4022028508SToomas Soome 
4122028508SToomas Soome #include "zfsimpl.h"
4222028508SToomas Soome #include "zfssubr.c"
4322028508SToomas Soome 
4422028508SToomas Soome 
4522028508SToomas Soome struct zfsmount {
4622028508SToomas Soome 	const spa_t	*spa;
4722028508SToomas Soome 	objset_phys_t	objset;
4822028508SToomas Soome 	uint64_t	rootobj;
4922028508SToomas Soome };
5022028508SToomas Soome 
5122028508SToomas Soome /*
5222028508SToomas Soome  * The indirect_child_t represents the vdev that we will read from, when we
5322028508SToomas Soome  * need to read all copies of the data (e.g. for scrub or reconstruction).
5422028508SToomas Soome  * For plain (non-mirror) top-level vdevs (i.e. is_vdev is not a mirror),
5522028508SToomas Soome  * ic_vdev is the same as is_vdev.  However, for mirror top-level vdevs,
5622028508SToomas Soome  * ic_vdev is a child of the mirror.
5722028508SToomas Soome  */
5822028508SToomas Soome typedef struct indirect_child {
5922028508SToomas Soome 	void *ic_data;
6022028508SToomas Soome 	vdev_t *ic_vdev;
6122028508SToomas Soome } indirect_child_t;
6222028508SToomas Soome 
6322028508SToomas Soome /*
6422028508SToomas Soome  * The indirect_split_t represents one mapped segment of an i/o to the
6522028508SToomas Soome  * indirect vdev. For non-split (contiguously-mapped) blocks, there will be
6622028508SToomas Soome  * only one indirect_split_t, with is_split_offset==0 and is_size==io_size.
6722028508SToomas Soome  * For split blocks, there will be several of these.
6822028508SToomas Soome  */
6922028508SToomas Soome typedef struct indirect_split {
7022028508SToomas Soome 	list_node_t is_node; /* link on iv_splits */
7122028508SToomas Soome 
7222028508SToomas Soome 	/*
7322028508SToomas Soome 	 * is_split_offset is the offset into the i/o.
7422028508SToomas Soome 	 * This is the sum of the previous splits' is_size's.
7522028508SToomas Soome 	 */
7622028508SToomas Soome 	uint64_t is_split_offset;
7722028508SToomas Soome 
7822028508SToomas Soome 	vdev_t *is_vdev; /* top-level vdev */
7922028508SToomas Soome 	uint64_t is_target_offset; /* offset on is_vdev */
8022028508SToomas Soome 	uint64_t is_size;
8122028508SToomas Soome 	int is_children; /* number of entries in is_child[] */
8222028508SToomas Soome 
8322028508SToomas Soome 	/*
8422028508SToomas Soome 	 * is_good_child is the child that we are currently using to
8522028508SToomas Soome 	 * attempt reconstruction.
8622028508SToomas Soome 	 */
8722028508SToomas Soome 	int is_good_child;
8822028508SToomas Soome 
8922028508SToomas Soome 	indirect_child_t is_child[1]; /* variable-length */
9022028508SToomas Soome } indirect_split_t;
9122028508SToomas Soome 
9222028508SToomas Soome /*
9322028508SToomas Soome  * The indirect_vsd_t is associated with each i/o to the indirect vdev.
9422028508SToomas Soome  * It is the "Vdev-Specific Data" in the zio_t's io_vsd.
9522028508SToomas Soome  */
9622028508SToomas Soome typedef struct indirect_vsd {
9722028508SToomas Soome 	boolean_t iv_split_block;
9822028508SToomas Soome 	boolean_t iv_reconstruct;
9922028508SToomas Soome 
10022028508SToomas Soome 	list_t iv_splits; /* list of indirect_split_t's */
10122028508SToomas Soome } indirect_vsd_t;
10222028508SToomas Soome 
10322028508SToomas Soome /*
10422028508SToomas Soome  * List of all vdevs, chained through v_alllink.
10522028508SToomas Soome  */
10622028508SToomas Soome static vdev_list_t zfs_vdevs;
10722028508SToomas Soome 
10822028508SToomas Soome /*
10922028508SToomas Soome  * List of ZFS features supported for read
11022028508SToomas Soome  */
11122028508SToomas Soome static const char *features_for_read[] = {
11222028508SToomas Soome 	"org.illumos:lz4_compress",
11322028508SToomas Soome 	"com.delphix:hole_birth",
11422028508SToomas Soome 	"com.delphix:extensible_dataset",
11522028508SToomas Soome 	"com.delphix:embedded_data",
11622028508SToomas Soome 	"org.open-zfs:large_blocks",
11722028508SToomas Soome 	"org.illumos:sha512",
11822028508SToomas Soome 	"org.illumos:skein",
11922028508SToomas Soome 	"org.illumos:edonr",
12022028508SToomas Soome 	"org.zfsonlinux:large_dnode",
12122028508SToomas Soome 	"com.joyent:multi_vdev_crash_dump",
12222028508SToomas Soome 	"com.delphix:spacemap_histogram",
12322028508SToomas Soome 	"com.delphix:zpool_checkpoint",
12422028508SToomas Soome 	"com.delphix:spacemap_v2",
12522028508SToomas Soome 	"com.datto:encryption",
12622028508SToomas Soome 	"com.datto:bookmark_v2",
12722028508SToomas Soome 	"org.zfsonlinux:allocation_classes",
12822028508SToomas Soome 	"com.datto:resilver_defer",
12922028508SToomas Soome 	"com.delphix:device_removal",
13022028508SToomas Soome 	"com.delphix:obsolete_counts",
13122028508SToomas Soome 	NULL
13222028508SToomas Soome };
13322028508SToomas Soome 
13422028508SToomas Soome /*
13522028508SToomas Soome  * List of all pools, chained through spa_link.
13622028508SToomas Soome  */
13722028508SToomas Soome static spa_list_t zfs_pools;
13822028508SToomas Soome 
13922028508SToomas Soome static const dnode_phys_t *dnode_cache_obj;
14022028508SToomas Soome static uint64_t dnode_cache_bn;
14122028508SToomas Soome static char *dnode_cache_buf;
14222028508SToomas Soome 
14322028508SToomas Soome static int zio_read(const spa_t *spa, const blkptr_t *bp, void *buf);
14422028508SToomas Soome static int zfs_get_root(const spa_t *spa, uint64_t *objid);
14522028508SToomas Soome static int zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result);
14622028508SToomas Soome static int zap_lookup(const spa_t *spa, const dnode_phys_t *dnode,
14722028508SToomas Soome     const char *name, uint64_t integer_size, uint64_t num_integers,
14822028508SToomas Soome     void *value);
14922028508SToomas Soome static int objset_get_dnode(const spa_t *, const objset_phys_t *, uint64_t,
15022028508SToomas Soome     dnode_phys_t *);
15122028508SToomas Soome static int dnode_read(const spa_t *, const dnode_phys_t *, off_t, void *,
15222028508SToomas Soome     size_t);
15322028508SToomas Soome static int vdev_indirect_read(vdev_t *, const blkptr_t *, void *, off_t,
15422028508SToomas Soome     size_t);
15522028508SToomas Soome static int vdev_mirror_read(vdev_t *, const blkptr_t *, void *, off_t,
15622028508SToomas Soome     size_t);
15722028508SToomas Soome 
15822028508SToomas Soome static void
zfs_init(void)15922028508SToomas Soome zfs_init(void)
16022028508SToomas Soome {
16122028508SToomas Soome 	STAILQ_INIT(&zfs_vdevs);
16222028508SToomas Soome 	STAILQ_INIT(&zfs_pools);
16322028508SToomas Soome 
16422028508SToomas Soome 	dnode_cache_buf = malloc(SPA_MAXBLOCKSIZE);
16522028508SToomas Soome 
16622028508SToomas Soome 	zfs_init_crc();
16722028508SToomas Soome }
16822028508SToomas Soome 
16922028508SToomas Soome static int
nvlist_check_features_for_read(nvlist_t * nvl)17022028508SToomas Soome nvlist_check_features_for_read(nvlist_t *nvl)
17122028508SToomas Soome {
17222028508SToomas Soome 	nvlist_t *features = NULL;
17322028508SToomas Soome 	nvs_data_t *data;
17422028508SToomas Soome 	nvp_header_t *nvp;
17522028508SToomas Soome 	nv_string_t *nvp_name;
17622028508SToomas Soome 	int rc;
17722028508SToomas Soome 
17822028508SToomas Soome 	/*
17922028508SToomas Soome 	 * We may have all features disabled.
18022028508SToomas Soome 	 */
18122028508SToomas Soome 	rc = nvlist_find(nvl, ZPOOL_CONFIG_FEATURES_FOR_READ,
18222028508SToomas Soome 	    DATA_TYPE_NVLIST, NULL, &features, NULL);
18322028508SToomas Soome 	switch (rc) {
18422028508SToomas Soome 	case 0:
18522028508SToomas Soome 		break;		/* Continue with checks */
18622028508SToomas Soome 
18722028508SToomas Soome 	case ENOENT:
18822028508SToomas Soome 		return (0);	/* All features are disabled */
18922028508SToomas Soome 
19022028508SToomas Soome 	default:
19122028508SToomas Soome 		return (rc);	/* Error while reading nvlist */
19222028508SToomas Soome 	}
19322028508SToomas Soome 
19422028508SToomas Soome 	data = (nvs_data_t *)features->nv_data;
19522028508SToomas Soome 	nvp = &data->nvl_pair;	/* first pair in nvlist */
19622028508SToomas Soome 
19722028508SToomas Soome 	while (nvp->encoded_size != 0 && nvp->decoded_size != 0) {
19822028508SToomas Soome 		int i, found;
19922028508SToomas Soome 
20022028508SToomas Soome 		nvp_name = (nv_string_t *)((uintptr_t)nvp + sizeof (*nvp));
20122028508SToomas Soome 		found = 0;
20222028508SToomas Soome 
20322028508SToomas Soome 		for (i = 0; features_for_read[i] != NULL; i++) {
20422028508SToomas Soome 			if (memcmp(nvp_name->nv_data, features_for_read[i],
20522028508SToomas Soome 			    nvp_name->nv_size) == 0) {
20622028508SToomas Soome 				found = 1;
20722028508SToomas Soome 				break;
20822028508SToomas Soome 			}
20922028508SToomas Soome 		}
21022028508SToomas Soome 
21122028508SToomas Soome 		if (!found) {
21222028508SToomas Soome 			printf("ZFS: unsupported feature: %.*s\n",
21322028508SToomas Soome 			    nvp_name->nv_size, nvp_name->nv_data);
21422028508SToomas Soome 			rc = EIO;
21522028508SToomas Soome 		}
21622028508SToomas Soome 		nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size);
21722028508SToomas Soome 	}
21822028508SToomas Soome 	nvlist_destroy(features);
21922028508SToomas Soome 
22022028508SToomas Soome 	return (rc);
22122028508SToomas Soome }
22222028508SToomas Soome 
22322028508SToomas Soome static int
vdev_read_phys(vdev_t * vdev,const blkptr_t * bp,void * buf,off_t offset,size_t size)22422028508SToomas Soome vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf,
22522028508SToomas Soome     off_t offset, size_t size)
22622028508SToomas Soome {
22722028508SToomas Soome 	size_t psize;
22822028508SToomas Soome 	int rc;
22922028508SToomas Soome 
23022028508SToomas Soome 	if (vdev->v_phys_read == NULL)
23122028508SToomas Soome 		return (ENOTSUP);
23222028508SToomas Soome 
23322028508SToomas Soome 	if (bp) {
23422028508SToomas Soome 		psize = BP_GET_PSIZE(bp);
23522028508SToomas Soome 	} else {
23622028508SToomas Soome 		psize = size;
23722028508SToomas Soome 	}
23822028508SToomas Soome 
23922028508SToomas Soome 	rc = vdev->v_phys_read(vdev, vdev->v_priv, offset, buf, psize);
24022028508SToomas Soome 	if (rc == 0) {
24122028508SToomas Soome 		if (bp != NULL)
24222028508SToomas Soome 			rc = zio_checksum_verify(vdev->v_spa, bp, buf);
24322028508SToomas Soome 	}
24422028508SToomas Soome 
24522028508SToomas Soome 	return (rc);
24622028508SToomas Soome }
24722028508SToomas Soome 
24822028508SToomas Soome static int
vdev_write_phys(vdev_t * vdev,void * buf,off_t offset,size_t size)24922028508SToomas Soome vdev_write_phys(vdev_t *vdev, void *buf, off_t offset, size_t size)
25022028508SToomas Soome {
25122028508SToomas Soome 	if (vdev->v_phys_write == NULL)
25222028508SToomas Soome 		return (ENOTSUP);
25322028508SToomas Soome 
25422028508SToomas Soome 	return (vdev->v_phys_write(vdev, offset, buf, size));
25522028508SToomas Soome }
25622028508SToomas Soome 
25722028508SToomas Soome typedef struct remap_segment {
25822028508SToomas Soome 	vdev_t *rs_vd;
25922028508SToomas Soome 	uint64_t rs_offset;
26022028508SToomas Soome 	uint64_t rs_asize;
26122028508SToomas Soome 	uint64_t rs_split_offset;
26222028508SToomas Soome 	list_node_t rs_node;
26322028508SToomas Soome } remap_segment_t;
26422028508SToomas Soome 
26522028508SToomas Soome static remap_segment_t *
rs_alloc(vdev_t * vd,uint64_t offset,uint64_t asize,uint64_t split_offset)26622028508SToomas Soome rs_alloc(vdev_t *vd, uint64_t offset, uint64_t asize, uint64_t split_offset)
26722028508SToomas Soome {
26822028508SToomas Soome 	remap_segment_t *rs = malloc(sizeof (remap_segment_t));
26922028508SToomas Soome 
27022028508SToomas Soome 	if (rs != NULL) {
27122028508SToomas Soome 		rs->rs_vd = vd;
27222028508SToomas Soome 		rs->rs_offset = offset;
27322028508SToomas Soome 		rs->rs_asize = asize;
27422028508SToomas Soome 		rs->rs_split_offset = split_offset;
27522028508SToomas Soome 	}
27622028508SToomas Soome 
27722028508SToomas Soome 	return (rs);
27822028508SToomas Soome }
27922028508SToomas Soome 
28022028508SToomas Soome vdev_indirect_mapping_t *
vdev_indirect_mapping_open(spa_t * spa,objset_phys_t * os,uint64_t mapping_object)28122028508SToomas Soome vdev_indirect_mapping_open(spa_t *spa, objset_phys_t *os,
28222028508SToomas Soome     uint64_t mapping_object)
28322028508SToomas Soome {
28422028508SToomas Soome 	vdev_indirect_mapping_t *vim;
28522028508SToomas Soome 	vdev_indirect_mapping_phys_t *vim_phys;
28622028508SToomas Soome 	int rc;
28722028508SToomas Soome 
28822028508SToomas Soome 	vim = calloc(1, sizeof (*vim));
28922028508SToomas Soome 	if (vim == NULL)
29022028508SToomas Soome 		return (NULL);
29122028508SToomas Soome 
29222028508SToomas Soome 	vim->vim_dn = calloc(1, sizeof (*vim->vim_dn));
29322028508SToomas Soome 	if (vim->vim_dn == NULL) {
29422028508SToomas Soome 		free(vim);
29522028508SToomas Soome 		return (NULL);
29622028508SToomas Soome 	}
29722028508SToomas Soome 
29822028508SToomas Soome 	rc = objset_get_dnode(spa, os, mapping_object, vim->vim_dn);
29922028508SToomas Soome 	if (rc != 0) {
30022028508SToomas Soome 		free(vim->vim_dn);
30122028508SToomas Soome 		free(vim);
30222028508SToomas Soome 		return (NULL);
30322028508SToomas Soome 	}
30422028508SToomas Soome 
30522028508SToomas Soome 	vim->vim_spa = spa;
30622028508SToomas Soome 	vim->vim_phys = malloc(sizeof (*vim->vim_phys));
30722028508SToomas Soome 	if (vim->vim_phys == NULL) {
30822028508SToomas Soome 		free(vim->vim_dn);
30922028508SToomas Soome 		free(vim);
31022028508SToomas Soome 		return (NULL);
31122028508SToomas Soome 	}
31222028508SToomas Soome 
31322028508SToomas Soome 	vim_phys = (vdev_indirect_mapping_phys_t *)DN_BONUS(vim->vim_dn);
31422028508SToomas Soome 	*vim->vim_phys = *vim_phys;
31522028508SToomas Soome 
31622028508SToomas Soome 	vim->vim_objset = os;
31722028508SToomas Soome 	vim->vim_object = mapping_object;
31822028508SToomas Soome 	vim->vim_entries = NULL;
31922028508SToomas Soome 
32022028508SToomas Soome 	vim->vim_havecounts =
32122028508SToomas Soome 	    (vim->vim_dn->dn_bonuslen > VDEV_INDIRECT_MAPPING_SIZE_V0);
32222028508SToomas Soome 
32322028508SToomas Soome 	return (vim);
32422028508SToomas Soome }
32522028508SToomas Soome 
32622028508SToomas Soome /*
32722028508SToomas Soome  * Compare an offset with an indirect mapping entry; there are three
32822028508SToomas Soome  * possible scenarios:
32922028508SToomas Soome  *
33022028508SToomas Soome  *     1. The offset is "less than" the mapping entry; meaning the
33122028508SToomas Soome  *        offset is less than the source offset of the mapping entry. In
33222028508SToomas Soome  *        this case, there is no overlap between the offset and the
33322028508SToomas Soome  *        mapping entry and -1 will be returned.
33422028508SToomas Soome  *
33522028508SToomas Soome  *     2. The offset is "greater than" the mapping entry; meaning the
33622028508SToomas Soome  *        offset is greater than the mapping entry's source offset plus
33722028508SToomas Soome  *        the entry's size. In this case, there is no overlap between
33822028508SToomas Soome  *        the offset and the mapping entry and 1 will be returned.
33922028508SToomas Soome  *
34022028508SToomas Soome  *        NOTE: If the offset is actually equal to the entry's offset
34122028508SToomas Soome  *        plus size, this is considered to be "greater" than the entry,
34222028508SToomas Soome  *        and this case applies (i.e. 1 will be returned). Thus, the
34322028508SToomas Soome  *        entry's "range" can be considered to be inclusive at its
34422028508SToomas Soome  *        start, but exclusive at its end: e.g. [src, src + size).
34522028508SToomas Soome  *
34622028508SToomas Soome  *     3. The last case to consider is if the offset actually falls
34722028508SToomas Soome  *        within the mapping entry's range. If this is the case, the
34822028508SToomas Soome  *        offset is considered to be "equal to" the mapping entry and
34922028508SToomas Soome  *        0 will be returned.
35022028508SToomas Soome  *
35122028508SToomas Soome  *        NOTE: If the offset is equal to the entry's source offset,
35222028508SToomas Soome  *        this case applies and 0 will be returned. If the offset is
35322028508SToomas Soome  *        equal to the entry's source plus its size, this case does
35422028508SToomas Soome  *        *not* apply (see "NOTE" above for scenario 2), and 1 will be
35522028508SToomas Soome  *        returned.
35622028508SToomas Soome  */
35722028508SToomas Soome static int
dva_mapping_overlap_compare(const void * v_key,const void * v_array_elem)35822028508SToomas Soome dva_mapping_overlap_compare(const void *v_key, const void *v_array_elem)
35922028508SToomas Soome {
36022028508SToomas Soome 	const uint64_t *key = v_key;
36122028508SToomas Soome 	const vdev_indirect_mapping_entry_phys_t *array_elem =
36222028508SToomas Soome 	    v_array_elem;
36322028508SToomas Soome 	uint64_t src_offset = DVA_MAPPING_GET_SRC_OFFSET(array_elem);
36422028508SToomas Soome 
36522028508SToomas Soome 	if (*key < src_offset) {
36622028508SToomas Soome 		return (-1);
36722028508SToomas Soome 	} else if (*key < src_offset + DVA_GET_ASIZE(&array_elem->vimep_dst)) {
36822028508SToomas Soome 		return (0);
36922028508SToomas Soome 	} else {
37022028508SToomas Soome 		return (1);
37122028508SToomas Soome 	}
37222028508SToomas Soome }
37322028508SToomas Soome 
37422028508SToomas Soome /*
37522028508SToomas Soome  * Return array entry.
37622028508SToomas Soome  */
37722028508SToomas Soome static vdev_indirect_mapping_entry_phys_t *
vdev_indirect_mapping_entry(vdev_indirect_mapping_t * vim,uint64_t index)37822028508SToomas Soome vdev_indirect_mapping_entry(vdev_indirect_mapping_t *vim, uint64_t index)
37922028508SToomas Soome {
38022028508SToomas Soome 	uint64_t size;
38122028508SToomas Soome 	off_t offset = 0;
38222028508SToomas Soome 	int rc;
38322028508SToomas Soome 
38422028508SToomas Soome 	if (vim->vim_phys->vimp_num_entries == 0)
38522028508SToomas Soome 		return (NULL);
38622028508SToomas Soome 
38722028508SToomas Soome 	if (vim->vim_entries == NULL) {
38822028508SToomas Soome 		uint64_t bsize;
38922028508SToomas Soome 
39022028508SToomas Soome 		bsize = vim->vim_dn->dn_datablkszsec << SPA_MINBLOCKSHIFT;
39122028508SToomas Soome 		size = vim->vim_phys->vimp_num_entries *
39222028508SToomas Soome 		    sizeof (*vim->vim_entries);
39322028508SToomas Soome 		if (size > bsize) {
39422028508SToomas Soome 			size = bsize / sizeof (*vim->vim_entries);
39522028508SToomas Soome 			size *= sizeof (*vim->vim_entries);
39622028508SToomas Soome 		}
39722028508SToomas Soome 		vim->vim_entries = malloc(size);
39822028508SToomas Soome 		if (vim->vim_entries == NULL)
39922028508SToomas Soome 			return (NULL);
40022028508SToomas Soome 		vim->vim_num_entries = size / sizeof (*vim->vim_entries);
40122028508SToomas Soome 		offset = index * sizeof (*vim->vim_entries);
40222028508SToomas Soome 	}
40322028508SToomas Soome 
40422028508SToomas Soome 	/* We have data in vim_entries */
40522028508SToomas Soome 	if (offset == 0) {
40622028508SToomas Soome 		if (index >= vim->vim_entry_offset &&
40722028508SToomas Soome 		    index <= vim->vim_entry_offset + vim->vim_num_entries) {
40822028508SToomas Soome 			index -= vim->vim_entry_offset;
40922028508SToomas Soome 			return (&vim->vim_entries[index]);
41022028508SToomas Soome 		}
41122028508SToomas Soome 		offset = index * sizeof (*vim->vim_entries);
41222028508SToomas Soome 	}
41322028508SToomas Soome 
41422028508SToomas Soome 	vim->vim_entry_offset = index;
41522028508SToomas Soome 	size = vim->vim_num_entries * sizeof (*vim->vim_entries);
41622028508SToomas Soome 	rc = dnode_read(vim->vim_spa, vim->vim_dn, offset, vim->vim_entries,
41722028508SToomas Soome 	    size);
41822028508SToomas Soome 	if (rc != 0) {
41922028508SToomas Soome 		/* Read error, invalidate vim_entries. */
42022028508SToomas Soome 		free(vim->vim_entries);
42122028508SToomas Soome 		vim->vim_entries = NULL;
42222028508SToomas Soome 		return (NULL);
42322028508SToomas Soome 	}
42422028508SToomas Soome 	index -= vim->vim_entry_offset;
42522028508SToomas Soome 	return (&vim->vim_entries[index]);
42622028508SToomas Soome }
42722028508SToomas Soome 
42822028508SToomas Soome /*
42922028508SToomas Soome  * Returns the mapping entry for the given offset.
43022028508SToomas Soome  *
43122028508SToomas Soome  * It's possible that the given offset will not be in the mapping table
43222028508SToomas Soome  * (i.e. no mapping entries contain this offset), in which case, the
43322028508SToomas Soome  * return value value depends on the "next_if_missing" parameter.
43422028508SToomas Soome  *
43522028508SToomas Soome  * If the offset is not found in the table and "next_if_missing" is
43622028508SToomas Soome  * B_FALSE, then NULL will always be returned. The behavior is intended
43722028508SToomas Soome  * to allow consumers to get the entry corresponding to the offset
43822028508SToomas Soome  * parameter, iff the offset overlaps with an entry in the table.
43922028508SToomas Soome  *
44022028508SToomas Soome  * If the offset is not found in the table and "next_if_missing" is
44122028508SToomas Soome  * B_TRUE, then the entry nearest to the given offset will be returned,
44222028508SToomas Soome  * such that the entry's source offset is greater than the offset
44322028508SToomas Soome  * passed in (i.e. the "next" mapping entry in the table is returned, if
44422028508SToomas Soome  * the offset is missing from the table). If there are no entries whose
44522028508SToomas Soome  * source offset is greater than the passed in offset, NULL is returned.
44622028508SToomas Soome  */
44722028508SToomas Soome static vdev_indirect_mapping_entry_phys_t *
vdev_indirect_mapping_entry_for_offset(vdev_indirect_mapping_t * vim,uint64_t offset)44822028508SToomas Soome vdev_indirect_mapping_entry_for_offset(vdev_indirect_mapping_t *vim,
44922028508SToomas Soome     uint64_t offset)
45022028508SToomas Soome {
45122028508SToomas Soome 	ASSERT(vim->vim_phys->vimp_num_entries > 0);
45222028508SToomas Soome 
45322028508SToomas Soome 	vdev_indirect_mapping_entry_phys_t *entry;
45422028508SToomas Soome 
45522028508SToomas Soome 	uint64_t last = vim->vim_phys->vimp_num_entries - 1;
45622028508SToomas Soome 	uint64_t base = 0;
45722028508SToomas Soome 
45822028508SToomas Soome 	/*
45922028508SToomas Soome 	 * We don't define these inside of the while loop because we use
46022028508SToomas Soome 	 * their value in the case that offset isn't in the mapping.
46122028508SToomas Soome 	 */
46222028508SToomas Soome 	uint64_t mid;
46322028508SToomas Soome 	int result;
46422028508SToomas Soome 
46522028508SToomas Soome 	while (last >= base) {
46622028508SToomas Soome 		mid = base + ((last - base) >> 1);
46722028508SToomas Soome 
46822028508SToomas Soome 		entry = vdev_indirect_mapping_entry(vim, mid);
46922028508SToomas Soome 		if (entry == NULL)
47022028508SToomas Soome 			break;
47122028508SToomas Soome 		result = dva_mapping_overlap_compare(&offset, entry);
47222028508SToomas Soome 
47322028508SToomas Soome 		if (result == 0) {
47422028508SToomas Soome 			break;
47522028508SToomas Soome 		} else if (result < 0) {
47622028508SToomas Soome 			last = mid - 1;
47722028508SToomas Soome 		} else {
47822028508SToomas Soome 			base = mid + 1;
47922028508SToomas Soome 		}
48022028508SToomas Soome 	}
48122028508SToomas Soome 	return (entry);
48222028508SToomas Soome }
48322028508SToomas Soome 
48422028508SToomas Soome /*
48522028508SToomas Soome  * Given an indirect vdev and an extent on that vdev, it duplicates the
48622028508SToomas Soome  * physical entries of the indirect mapping that correspond to the extent
48722028508SToomas Soome  * to a new array and returns a pointer to it. In addition, copied_entries
48822028508SToomas Soome  * is populated with the number of mapping entries that were duplicated.
48922028508SToomas Soome  *
49022028508SToomas Soome  * Finally, since we are doing an allocation, it is up to the caller to
49122028508SToomas Soome  * free the array allocated in this function.
49222028508SToomas Soome  */
49322028508SToomas Soome vdev_indirect_mapping_entry_phys_t *
vdev_indirect_mapping_duplicate_adjacent_entries(vdev_t * vd,uint64_t offset,uint64_t asize,uint64_t * copied_entries)49422028508SToomas Soome vdev_indirect_mapping_duplicate_adjacent_entries(vdev_t *vd, uint64_t offset,
49522028508SToomas Soome     uint64_t asize, uint64_t *copied_entries)
49622028508SToomas Soome {
49722028508SToomas Soome 	vdev_indirect_mapping_entry_phys_t *duplicate_mappings = NULL;
49822028508SToomas Soome 	vdev_indirect_mapping_t *vim = vd->v_mapping;
49922028508SToomas Soome 	uint64_t entries = 0;
50022028508SToomas Soome 
50122028508SToomas Soome 	vdev_indirect_mapping_entry_phys_t *first_mapping =
50222028508SToomas Soome 	    vdev_indirect_mapping_entry_for_offset(vim, offset);
50322028508SToomas Soome 	ASSERT3P(first_mapping, !=, NULL);
50422028508SToomas Soome 
50522028508SToomas Soome 	vdev_indirect_mapping_entry_phys_t *m = first_mapping;
50622028508SToomas Soome 	while (asize > 0) {
50722028508SToomas Soome 		uint64_t size = DVA_GET_ASIZE(&m->vimep_dst);
50822028508SToomas Soome 		uint64_t inner_offset = offset - DVA_MAPPING_GET_SRC_OFFSET(m);
50922028508SToomas Soome 		uint64_t inner_size = MIN(asize, size - inner_offset);
51022028508SToomas Soome 
51122028508SToomas Soome 		offset += inner_size;
51222028508SToomas Soome 		asize -= inner_size;
51322028508SToomas Soome 		entries++;
51422028508SToomas Soome 		m++;
51522028508SToomas Soome 	}
51622028508SToomas Soome 
51722028508SToomas Soome 	size_t copy_length = entries * sizeof (*first_mapping);
51822028508SToomas Soome 	duplicate_mappings = malloc(copy_length);
51922028508SToomas Soome 	if (duplicate_mappings != NULL)
52022028508SToomas Soome 		bcopy(first_mapping, duplicate_mappings, copy_length);
52122028508SToomas Soome 	else
52222028508SToomas Soome 		entries = 0;
52322028508SToomas Soome 
52422028508SToomas Soome 	*copied_entries = entries;
52522028508SToomas Soome 
52622028508SToomas Soome 	return (duplicate_mappings);
52722028508SToomas Soome }
52822028508SToomas Soome 
52922028508SToomas Soome static vdev_t *
vdev_lookup_top(spa_t * spa,uint64_t vdev)53022028508SToomas Soome vdev_lookup_top(spa_t *spa, uint64_t vdev)
53122028508SToomas Soome {
53222028508SToomas Soome 	vdev_t *rvd;
53322028508SToomas Soome 	vdev_list_t *vlist;
53422028508SToomas Soome 
53522028508SToomas Soome 	vlist = &spa->spa_root_vdev->v_children;
53622028508SToomas Soome 	STAILQ_FOREACH(rvd, vlist, v_childlink)
53722028508SToomas Soome 		if (rvd->v_id == vdev)
53822028508SToomas Soome 			break;
53922028508SToomas Soome 
54022028508SToomas Soome 	return (rvd);
54122028508SToomas Soome }
54222028508SToomas Soome 
54322028508SToomas Soome /*
54422028508SToomas Soome  * This is a callback for vdev_indirect_remap() which allocates an
54522028508SToomas Soome  * indirect_split_t for each split segment and adds it to iv_splits.
54622028508SToomas Soome  */
54722028508SToomas Soome static void
vdev_indirect_gather_splits(uint64_t split_offset,vdev_t * vd,uint64_t offset,uint64_t size,void * arg)54822028508SToomas Soome vdev_indirect_gather_splits(uint64_t split_offset, vdev_t *vd, uint64_t offset,
54922028508SToomas Soome     uint64_t size, void *arg)
55022028508SToomas Soome {
55122028508SToomas Soome 	int n = 1;
55222028508SToomas Soome 	zio_t *zio = arg;
55322028508SToomas Soome 	indirect_vsd_t *iv = zio->io_vsd;
55422028508SToomas Soome 
55522028508SToomas Soome 	if (vd->v_read == vdev_indirect_read)
55622028508SToomas Soome 		return;
55722028508SToomas Soome 
55822028508SToomas Soome 	if (vd->v_read == vdev_mirror_read)
55922028508SToomas Soome 		n = vd->v_nchildren;
56022028508SToomas Soome 
56122028508SToomas Soome 	indirect_split_t *is =
56222028508SToomas Soome 	    malloc(offsetof(indirect_split_t, is_child[n]));
56322028508SToomas Soome 	if (is == NULL) {
56422028508SToomas Soome 		zio->io_error = ENOMEM;
56522028508SToomas Soome 		return;
56622028508SToomas Soome 	}
56722028508SToomas Soome 	bzero(is, offsetof(indirect_split_t, is_child[n]));
56822028508SToomas Soome 
56922028508SToomas Soome 	is->is_children = n;
57022028508SToomas Soome 	is->is_size = size;
57122028508SToomas Soome 	is->is_split_offset = split_offset;
57222028508SToomas Soome 	is->is_target_offset = offset;
57322028508SToomas Soome 	is->is_vdev = vd;
57422028508SToomas Soome 
57522028508SToomas Soome 	/*
57622028508SToomas Soome 	 * Note that we only consider multiple copies of the data for
57722028508SToomas Soome 	 * *mirror* vdevs.  We don't for "replacing" or "spare" vdevs, even
57822028508SToomas Soome 	 * though they use the same ops as mirror, because there's only one
57922028508SToomas Soome 	 * "good" copy under the replacing/spare.
58022028508SToomas Soome 	 */
58122028508SToomas Soome 	if (vd->v_read == vdev_mirror_read) {
58222028508SToomas Soome 		int i = 0;
58322028508SToomas Soome 		vdev_t *kid;
58422028508SToomas Soome 
58522028508SToomas Soome 		STAILQ_FOREACH(kid, &vd->v_children, v_childlink) {
58622028508SToomas Soome 			is->is_child[i++].ic_vdev = kid;
58722028508SToomas Soome 		}
58822028508SToomas Soome 	} else {
58922028508SToomas Soome 		is->is_child[0].ic_vdev = vd;
59022028508SToomas Soome 	}
59122028508SToomas Soome 
59222028508SToomas Soome 	list_insert_tail(&iv->iv_splits, is);
59322028508SToomas Soome }
59422028508SToomas Soome 
59522028508SToomas Soome static void
vdev_indirect_remap(vdev_t * vd,uint64_t offset,uint64_t asize,void * arg)59622028508SToomas Soome vdev_indirect_remap(vdev_t *vd, uint64_t offset, uint64_t asize, void *arg)
59722028508SToomas Soome {
59822028508SToomas Soome 	list_t stack;
59922028508SToomas Soome 	spa_t *spa = vd->v_spa;
60022028508SToomas Soome 	zio_t *zio = arg;
60122028508SToomas Soome 	remap_segment_t *rs;
60222028508SToomas Soome 
60322028508SToomas Soome 	list_create(&stack, sizeof (remap_segment_t),
60422028508SToomas Soome 	    offsetof(remap_segment_t, rs_node));
60522028508SToomas Soome 
60622028508SToomas Soome 	rs = rs_alloc(vd, offset, asize, 0);
60722028508SToomas Soome 	if (rs == NULL) {
60822028508SToomas Soome 		printf("vdev_indirect_remap: out of memory.\n");
60922028508SToomas Soome 		zio->io_error = ENOMEM;
61022028508SToomas Soome 	}
61122028508SToomas Soome 	for (; rs != NULL; rs = list_remove_head(&stack)) {
61222028508SToomas Soome 		vdev_t *v = rs->rs_vd;
61322028508SToomas Soome 		uint64_t num_entries = 0;
61422028508SToomas Soome 		/* vdev_indirect_mapping_t *vim = v->v_mapping; */
61522028508SToomas Soome 		vdev_indirect_mapping_entry_phys_t *mapping =
61622028508SToomas Soome 		    vdev_indirect_mapping_duplicate_adjacent_entries(v,
61722028508SToomas Soome 		    rs->rs_offset, rs->rs_asize, &num_entries);
61822028508SToomas Soome 
61922028508SToomas Soome 		if (num_entries == 0)
62022028508SToomas Soome 			zio->io_error = ENOMEM;
62122028508SToomas Soome 
62222028508SToomas Soome 		for (uint64_t i = 0; i < num_entries; i++) {
62322028508SToomas Soome 			vdev_indirect_mapping_entry_phys_t *m = &mapping[i];
62422028508SToomas Soome 			uint64_t size = DVA_GET_ASIZE(&m->vimep_dst);
62522028508SToomas Soome 			uint64_t dst_offset = DVA_GET_OFFSET(&m->vimep_dst);
62622028508SToomas Soome 			uint64_t dst_vdev = DVA_GET_VDEV(&m->vimep_dst);
62722028508SToomas Soome 			uint64_t inner_offset = rs->rs_offset -
62822028508SToomas Soome 			    DVA_MAPPING_GET_SRC_OFFSET(m);
62922028508SToomas Soome 			uint64_t inner_size =
63022028508SToomas Soome 			    MIN(rs->rs_asize, size - inner_offset);
63122028508SToomas Soome 			vdev_t *dst_v = vdev_lookup_top(spa, dst_vdev);
63222028508SToomas Soome 
63322028508SToomas Soome 			if (dst_v->v_read == vdev_indirect_read) {
63422028508SToomas Soome 				remap_segment_t *o;
63522028508SToomas Soome 
63622028508SToomas Soome 				o = rs_alloc(dst_v, dst_offset + inner_offset,
63722028508SToomas Soome 				    inner_size, rs->rs_split_offset);
63822028508SToomas Soome 				if (o == NULL) {
63922028508SToomas Soome 					printf("vdev_indirect_remap: "
64022028508SToomas Soome 					    "out of memory.\n");
64122028508SToomas Soome 					zio->io_error = ENOMEM;
64222028508SToomas Soome 					break;
64322028508SToomas Soome 				}
64422028508SToomas Soome 
64522028508SToomas Soome 				list_insert_head(&stack, o);
64622028508SToomas Soome 			}
64722028508SToomas Soome 			vdev_indirect_gather_splits(rs->rs_split_offset, dst_v,
64822028508SToomas Soome 			    dst_offset + inner_offset,
64922028508SToomas Soome 			    inner_size, arg);
65022028508SToomas Soome 
65122028508SToomas Soome 			/*
65222028508SToomas Soome 			 * vdev_indirect_gather_splits can have memory
65322028508SToomas Soome 			 * allocation error, we can not recover from it.
65422028508SToomas Soome 			 */
65522028508SToomas Soome 			if (zio->io_error != 0)
65622028508SToomas Soome 				break;
65722028508SToomas Soome 			rs->rs_offset += inner_size;
65822028508SToomas Soome 			rs->rs_asize -= inner_size;
65922028508SToomas Soome 			rs->rs_split_offset += inner_size;
66022028508SToomas Soome 		}
66122028508SToomas Soome 
66222028508SToomas Soome 		free(mapping);
66322028508SToomas Soome 		free(rs);
66422028508SToomas Soome 		if (zio->io_error != 0)
66522028508SToomas Soome 			break;
66622028508SToomas Soome 	}
66722028508SToomas Soome 
66822028508SToomas Soome 	list_destroy(&stack);
66922028508SToomas Soome }
67022028508SToomas Soome 
67122028508SToomas Soome static void
vdev_indirect_map_free(zio_t * zio)67222028508SToomas Soome vdev_indirect_map_free(zio_t *zio)
67322028508SToomas Soome {
67422028508SToomas Soome 	indirect_vsd_t *iv = zio->io_vsd;
67522028508SToomas Soome 	indirect_split_t *is;
67622028508SToomas Soome 
67722028508SToomas Soome 	while ((is = list_head(&iv->iv_splits)) != NULL) {
67822028508SToomas Soome 		for (int c = 0; c < is->is_children; c++) {
67922028508SToomas Soome 			indirect_child_t *ic = &is->is_child[c];
68022028508SToomas Soome 			free(ic->ic_data);
68122028508SToomas Soome 		}
68222028508SToomas Soome 		list_remove(&iv->iv_splits, is);
68322028508SToomas Soome 		free(is);
68422028508SToomas Soome 	}
68522028508SToomas Soome 	free(iv);
68622028508SToomas Soome }
68722028508SToomas Soome 
68822028508SToomas Soome static int
vdev_indirect_read(vdev_t * vdev,const blkptr_t * bp,void * buf,off_t offset,size_t bytes)68922028508SToomas Soome vdev_indirect_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
69022028508SToomas Soome     off_t offset, size_t bytes)
69122028508SToomas Soome {
69222028508SToomas Soome 	zio_t zio;
69322028508SToomas Soome 	spa_t *spa = vdev->v_spa;
69422028508SToomas Soome 	indirect_vsd_t *iv;
69522028508SToomas Soome 	indirect_split_t *first;
69622028508SToomas Soome 	int rc = EIO;
69722028508SToomas Soome 
69822028508SToomas Soome 	iv = calloc(1, sizeof (*iv));
69922028508SToomas Soome 	if (iv == NULL)
70022028508SToomas Soome 		return (ENOMEM);
70122028508SToomas Soome 
70222028508SToomas Soome 	list_create(&iv->iv_splits,
70322028508SToomas Soome 	    sizeof (indirect_split_t), offsetof(indirect_split_t, is_node));
70422028508SToomas Soome 
70522028508SToomas Soome 	bzero(&zio, sizeof (zio));
70622028508SToomas Soome 	zio.io_spa = spa;
70722028508SToomas Soome 	zio.io_bp = (blkptr_t *)bp;
70822028508SToomas Soome 	zio.io_data = buf;
70922028508SToomas Soome 	zio.io_size = bytes;
71022028508SToomas Soome 	zio.io_offset = offset;
71122028508SToomas Soome 	zio.io_vd = vdev;
71222028508SToomas Soome 	zio.io_vsd = iv;
71322028508SToomas Soome 
71422028508SToomas Soome 	if (vdev->v_mapping == NULL) {
71522028508SToomas Soome 		vdev_indirect_config_t *vic;
71622028508SToomas Soome 
71722028508SToomas Soome 		vic = &vdev->vdev_indirect_config;
71822028508SToomas Soome 		vdev->v_mapping = vdev_indirect_mapping_open(spa,
71922028508SToomas Soome 		    &spa->spa_mos, vic->vic_mapping_object);
72022028508SToomas Soome 	}
72122028508SToomas Soome 
72222028508SToomas Soome 	vdev_indirect_remap(vdev, offset, bytes, &zio);
72322028508SToomas Soome 	if (zio.io_error != 0)
72422028508SToomas Soome 		return (zio.io_error);
72522028508SToomas Soome 
72622028508SToomas Soome 	first = list_head(&iv->iv_splits);
72722028508SToomas Soome 	if (first->is_size == zio.io_size) {
72822028508SToomas Soome 		/*
72922028508SToomas Soome 		 * This is not a split block; we are pointing to the entire
73022028508SToomas Soome 		 * data, which will checksum the same as the original data.
73122028508SToomas Soome 		 * Pass the BP down so that the child i/o can verify the
73222028508SToomas Soome 		 * checksum, and try a different location if available
73322028508SToomas Soome 		 * (e.g. on a mirror).
73422028508SToomas Soome 		 *
73522028508SToomas Soome 		 * While this special case could be handled the same as the
73622028508SToomas Soome 		 * general (split block) case, doing it this way ensures
73722028508SToomas Soome 		 * that the vast majority of blocks on indirect vdevs
73822028508SToomas Soome 		 * (which are not split) are handled identically to blocks
73922028508SToomas Soome 		 * on non-indirect vdevs.  This allows us to be less strict
74022028508SToomas Soome 		 * about performance in the general (but rare) case.
74122028508SToomas Soome 		 */
74222028508SToomas Soome 		rc = first->is_vdev->v_read(first->is_vdev, zio.io_bp,
74322028508SToomas Soome 		    zio.io_data, first->is_target_offset, bytes);
74422028508SToomas Soome 	} else {
74522028508SToomas Soome 		iv->iv_split_block = B_TRUE;
74622028508SToomas Soome 		/*
74722028508SToomas Soome 		 * Read one copy of each split segment, from the
74822028508SToomas Soome 		 * top-level vdev.  Since we don't know the
74922028508SToomas Soome 		 * checksum of each split individually, the child
75022028508SToomas Soome 		 * zio can't ensure that we get the right data.
75122028508SToomas Soome 		 * E.g. if it's a mirror, it will just read from a
75222028508SToomas Soome 		 * random (healthy) leaf vdev.  We have to verify
75322028508SToomas Soome 		 * the checksum in vdev_indirect_io_done().
75422028508SToomas Soome 		 */
75522028508SToomas Soome 		for (indirect_split_t *is = list_head(&iv->iv_splits);
75622028508SToomas Soome 		    is != NULL; is = list_next(&iv->iv_splits, is)) {
75722028508SToomas Soome 			char *ptr = zio.io_data;
75822028508SToomas Soome 
75922028508SToomas Soome 			rc = is->is_vdev->v_read(is->is_vdev, zio.io_bp,
76022028508SToomas Soome 			    ptr + is->is_split_offset, is->is_target_offset,
76122028508SToomas Soome 			    is->is_size);
76222028508SToomas Soome 		}
76322028508SToomas Soome 		if (zio_checksum_verify(spa, zio.io_bp, zio.io_data))
76422028508SToomas Soome 			rc = ECKSUM;
76522028508SToomas Soome 		else
76622028508SToomas Soome 			rc = 0;
76722028508SToomas Soome 	}
76822028508SToomas Soome 
76922028508SToomas Soome 	vdev_indirect_map_free(&zio);
77022028508SToomas Soome 	if (rc == 0)
77122028508SToomas Soome 		rc = zio.io_error;
77222028508SToomas Soome 
77322028508SToomas Soome 	return (rc);
77422028508SToomas Soome }
77522028508SToomas Soome 
77622028508SToomas Soome static int
vdev_disk_read(vdev_t * vdev,const blkptr_t * bp,void * buf,off_t offset,size_t bytes)77722028508SToomas Soome vdev_disk_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
77822028508SToomas Soome     off_t offset, size_t bytes)
77922028508SToomas Soome {
78022028508SToomas Soome 
78122028508SToomas Soome 	return (vdev_read_phys(vdev, bp, buf,
78222028508SToomas Soome 	    offset + VDEV_LABEL_START_SIZE, bytes));
78322028508SToomas Soome }
78422028508SToomas Soome 
78522028508SToomas Soome static int
vdev_missing_read(vdev_t * vdev __unused,const blkptr_t * bp __unused,void * buf __unused,off_t offset __unused,size_t bytes __unused)78622028508SToomas Soome vdev_missing_read(vdev_t *vdev __unused, const blkptr_t *bp __unused,
78722028508SToomas Soome     void *buf __unused, off_t offset __unused, size_t bytes __unused)
78822028508SToomas Soome {
78922028508SToomas Soome 
79022028508SToomas Soome 	return (ENOTSUP);
79122028508SToomas Soome }
79222028508SToomas Soome 
79322028508SToomas Soome static int
vdev_mirror_read(vdev_t * vdev,const blkptr_t * bp,void * buf,off_t offset,size_t bytes)79422028508SToomas Soome vdev_mirror_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
79522028508SToomas Soome     off_t offset, size_t bytes)
79622028508SToomas Soome {
79722028508SToomas Soome 	vdev_t *kid;
79822028508SToomas Soome 	int rc;
79922028508SToomas Soome 
80022028508SToomas Soome 	rc = EIO;
80122028508SToomas Soome 	STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
80222028508SToomas Soome 		if (kid->v_state != VDEV_STATE_HEALTHY)
80322028508SToomas Soome 			continue;
80422028508SToomas Soome 		rc = kid->v_read(kid, bp, buf, offset, bytes);
80522028508SToomas Soome 		if (!rc)
80622028508SToomas Soome 			return (0);
80722028508SToomas Soome 	}
80822028508SToomas Soome 
80922028508SToomas Soome 	return (rc);
81022028508SToomas Soome }
81122028508SToomas Soome 
81222028508SToomas Soome static int
vdev_replacing_read(vdev_t * vdev,const blkptr_t * bp,void * buf,off_t offset,size_t bytes)81322028508SToomas Soome vdev_replacing_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
81422028508SToomas Soome     off_t offset, size_t bytes)
81522028508SToomas Soome {
81622028508SToomas Soome 	vdev_t *kid;
81722028508SToomas Soome 
81822028508SToomas Soome 	/*
81922028508SToomas Soome 	 * Here we should have two kids:
82022028508SToomas Soome 	 * First one which is the one we are replacing and we can trust
82122028508SToomas Soome 	 * only this one to have valid data, but it might not be present.
82222028508SToomas Soome 	 * Second one is that one we are replacing with. It is most likely
82322028508SToomas Soome 	 * healthy, but we can't trust it has needed data, so we won't use it.
82422028508SToomas Soome 	 */
82522028508SToomas Soome 	kid = STAILQ_FIRST(&vdev->v_children);
82622028508SToomas Soome 	if (kid == NULL)
82722028508SToomas Soome 		return (EIO);
82822028508SToomas Soome 	if (kid->v_state != VDEV_STATE_HEALTHY)
82922028508SToomas Soome 		return (EIO);
83022028508SToomas Soome 	return (kid->v_read(kid, bp, buf, offset, bytes));
83122028508SToomas Soome }
83222028508SToomas Soome 
83322028508SToomas Soome static vdev_t *
vdev_find(uint64_t guid)83422028508SToomas Soome vdev_find(uint64_t guid)
83522028508SToomas Soome {
83622028508SToomas Soome 	vdev_t *vdev;
83722028508SToomas Soome 
83822028508SToomas Soome 	STAILQ_FOREACH(vdev, &zfs_vdevs, v_alllink)
83922028508SToomas Soome 		if (vdev->v_guid == guid)
84022028508SToomas Soome 			return (vdev);
84122028508SToomas Soome 
84222028508SToomas Soome 	return (0);
84322028508SToomas Soome }
84422028508SToomas Soome 
84522028508SToomas Soome static vdev_t *
vdev_create(uint64_t guid,vdev_read_t * vdev_read)84622028508SToomas Soome vdev_create(uint64_t guid, vdev_read_t *vdev_read)
84722028508SToomas Soome {
84822028508SToomas Soome 	vdev_t *vdev;
84922028508SToomas Soome 	vdev_indirect_config_t *vic;
85022028508SToomas Soome 
85122028508SToomas Soome 	vdev = calloc(1, sizeof (vdev_t));
85222028508SToomas Soome 	if (vdev != NULL) {
85322028508SToomas Soome 		STAILQ_INIT(&vdev->v_children);
85422028508SToomas Soome 		vdev->v_guid = guid;
85522028508SToomas Soome 		vdev->v_read = vdev_read;
85622028508SToomas Soome 
85722028508SToomas Soome 		/*
85822028508SToomas Soome 		 * root vdev has no read function, we use this fact to
85922028508SToomas Soome 		 * skip setting up data we do not need for root vdev.
86022028508SToomas Soome 		 * We only point root vdev from spa.
86122028508SToomas Soome 		 */
86222028508SToomas Soome 		if (vdev_read != NULL) {
86322028508SToomas Soome 			vic = &vdev->vdev_indirect_config;
86422028508SToomas Soome 			vic->vic_prev_indirect_vdev = UINT64_MAX;
86522028508SToomas Soome 			STAILQ_INSERT_TAIL(&zfs_vdevs, vdev, v_alllink);
86622028508SToomas Soome 		}
86722028508SToomas Soome 	}
86822028508SToomas Soome 
86922028508SToomas Soome 	return (vdev);
87022028508SToomas Soome }
87122028508SToomas Soome 
87222028508SToomas Soome static void
vdev_set_initial_state(vdev_t * vdev,const nvlist_t * nvlist)87322028508SToomas Soome vdev_set_initial_state(vdev_t *vdev, const nvlist_t *nvlist)
87422028508SToomas Soome {
87522028508SToomas Soome 	uint64_t is_offline, is_faulted, is_degraded, is_removed, isnt_present;
87622028508SToomas Soome 	uint64_t is_log;
87722028508SToomas Soome 
87822028508SToomas Soome 	is_offline = is_removed = is_faulted = is_degraded = isnt_present = 0;
87922028508SToomas Soome 	is_log = 0;
88022028508SToomas Soome 	(void) nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, NULL,
88122028508SToomas Soome 	    &is_offline, NULL);
88222028508SToomas Soome 	(void) nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, NULL,
88322028508SToomas Soome 	    &is_removed, NULL);
88422028508SToomas Soome 	(void) nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, NULL,
88522028508SToomas Soome 	    &is_faulted, NULL);
88622028508SToomas Soome 	(void) nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64,
88722028508SToomas Soome 	    NULL, &is_degraded, NULL);
88822028508SToomas Soome 	(void) nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64,
88922028508SToomas Soome 	    NULL, &isnt_present, NULL);
89022028508SToomas Soome 	(void) nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64, NULL,
89122028508SToomas Soome 	    &is_log, NULL);
89222028508SToomas Soome 
89322028508SToomas Soome 	if (is_offline != 0)
89422028508SToomas Soome 		vdev->v_state = VDEV_STATE_OFFLINE;
89522028508SToomas Soome 	else if (is_removed != 0)
89622028508SToomas Soome 		vdev->v_state = VDEV_STATE_REMOVED;
89722028508SToomas Soome 	else if (is_faulted != 0)
89822028508SToomas Soome 		vdev->v_state = VDEV_STATE_FAULTED;
89922028508SToomas Soome 	else if (is_degraded != 0)
90022028508SToomas Soome 		vdev->v_state = VDEV_STATE_DEGRADED;
90122028508SToomas Soome 	else if (isnt_present != 0)
90222028508SToomas Soome 		vdev->v_state = VDEV_STATE_CANT_OPEN;
90322028508SToomas Soome 
90422028508SToomas Soome 	vdev->v_islog = is_log != 0;
90522028508SToomas Soome }
90622028508SToomas Soome 
90722028508SToomas Soome static int
vdev_init(uint64_t guid,const nvlist_t * nvlist,vdev_t ** vdevp)90822028508SToomas Soome vdev_init(uint64_t guid, const nvlist_t *nvlist, vdev_t **vdevp)
90922028508SToomas Soome {
91022028508SToomas Soome 	uint64_t id, ashift, asize, nparity;
91122028508SToomas Soome 	const char *path;
91222028508SToomas Soome 	const char *type;
91322028508SToomas Soome 	int len, pathlen;
91422028508SToomas Soome 	char *name;
91522028508SToomas Soome 	vdev_t *vdev;
91622028508SToomas Soome 
91722028508SToomas Soome 	if (nvlist_find(nvlist, ZPOOL_CONFIG_ID, DATA_TYPE_UINT64, NULL, &id,
91822028508SToomas Soome 	    NULL) ||
91922028508SToomas Soome 	    nvlist_find(nvlist, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING,
92022028508SToomas Soome 	    NULL, &type, &len)) {
92122028508SToomas Soome 		return (ENOENT);
92222028508SToomas Soome 	}
92322028508SToomas Soome 
92422028508SToomas Soome 	if (memcmp(type, VDEV_TYPE_MIRROR, len) != 0 &&
92522028508SToomas Soome 	    memcmp(type, VDEV_TYPE_DISK, len) != 0 &&
92622028508SToomas Soome #ifdef ZFS_TEST
92722028508SToomas Soome 	    memcmp(type, VDEV_TYPE_FILE, len) != 0 &&
92822028508SToomas Soome #endif
92922028508SToomas Soome 	    memcmp(type, VDEV_TYPE_RAIDZ, len) != 0 &&
93022028508SToomas Soome 	    memcmp(type, VDEV_TYPE_INDIRECT, len) != 0 &&
93122028508SToomas Soome 	    memcmp(type, VDEV_TYPE_REPLACING, len) != 0 &&
93222028508SToomas Soome 	    memcmp(type, VDEV_TYPE_HOLE, len) != 0) {
93322028508SToomas Soome 		printf("ZFS: can only boot from disk, mirror, raidz1, "
93422028508SToomas Soome 		    "raidz2 and raidz3 vdevs, got: %.*s\n", len, type);
93522028508SToomas Soome 		return (EIO);
93622028508SToomas Soome 	}
93722028508SToomas Soome 
93822028508SToomas Soome 	if (memcmp(type, VDEV_TYPE_MIRROR, len) == 0)
93922028508SToomas Soome 		vdev = vdev_create(guid, vdev_mirror_read);
94022028508SToomas Soome 	else if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0)
94122028508SToomas Soome 		vdev = vdev_create(guid, vdev_raidz_read);
94222028508SToomas Soome 	else if (memcmp(type, VDEV_TYPE_REPLACING, len) == 0)
94322028508SToomas Soome 		vdev = vdev_create(guid, vdev_replacing_read);
94422028508SToomas Soome 	else if (memcmp(type, VDEV_TYPE_INDIRECT, len) == 0) {
94522028508SToomas Soome 		vdev_indirect_config_t *vic;
94622028508SToomas Soome 
94722028508SToomas Soome 		vdev = vdev_create(guid, vdev_indirect_read);
94822028508SToomas Soome 		if (vdev != NULL) {
94922028508SToomas Soome 			vdev->v_state = VDEV_STATE_HEALTHY;
95022028508SToomas Soome 			vic = &vdev->vdev_indirect_config;
95122028508SToomas Soome 
95222028508SToomas Soome 			nvlist_find(nvlist,
95322028508SToomas Soome 			    ZPOOL_CONFIG_INDIRECT_OBJECT,
95422028508SToomas Soome 			    DATA_TYPE_UINT64,
95522028508SToomas Soome 			    NULL, &vic->vic_mapping_object, NULL);
95622028508SToomas Soome 			nvlist_find(nvlist,
95722028508SToomas Soome 			    ZPOOL_CONFIG_INDIRECT_BIRTHS,
95822028508SToomas Soome 			    DATA_TYPE_UINT64,
95922028508SToomas Soome 			    NULL, &vic->vic_births_object, NULL);
96022028508SToomas Soome 			nvlist_find(nvlist,
96122028508SToomas Soome 			    ZPOOL_CONFIG_PREV_INDIRECT_VDEV,
96222028508SToomas Soome 			    DATA_TYPE_UINT64,
96322028508SToomas Soome 			    NULL, &vic->vic_prev_indirect_vdev, NULL);
96422028508SToomas Soome 		}
96522028508SToomas Soome 	} else if (memcmp(type, VDEV_TYPE_HOLE, len) == 0) {
96622028508SToomas Soome 		vdev = vdev_create(guid, vdev_missing_read);
96722028508SToomas Soome 	} else {
96822028508SToomas Soome 		vdev = vdev_create(guid, vdev_disk_read);
96922028508SToomas Soome 	}
97022028508SToomas Soome 
97122028508SToomas Soome 	if (vdev == NULL)
97222028508SToomas Soome 		return (ENOMEM);
97322028508SToomas Soome 
97422028508SToomas Soome 	vdev_set_initial_state(vdev, nvlist);
97522028508SToomas Soome 	vdev->v_id = id;
97622028508SToomas Soome 	if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT,
97722028508SToomas Soome 	    DATA_TYPE_UINT64, NULL, &ashift, NULL) == 0)
97822028508SToomas Soome 		vdev->v_ashift = ashift;
97922028508SToomas Soome 
98022028508SToomas Soome 	if (nvlist_find(nvlist, ZPOOL_CONFIG_ASIZE,
98122028508SToomas Soome 	    DATA_TYPE_UINT64, NULL, &asize, NULL) == 0) {
98222028508SToomas Soome 		vdev->v_psize = asize +
98322028508SToomas Soome 		    VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
98422028508SToomas Soome 	}
98522028508SToomas Soome 
98622028508SToomas Soome 	if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY,
98722028508SToomas Soome 	    DATA_TYPE_UINT64, NULL, &nparity, NULL) == 0)
98822028508SToomas Soome 		vdev->v_nparity = nparity;
98922028508SToomas Soome 
99022028508SToomas Soome 	if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH,
99122028508SToomas Soome 	    DATA_TYPE_STRING, NULL, &path, &pathlen) == 0) {
99222028508SToomas Soome 		char prefix[] = "/dev/dsk/";
99322028508SToomas Soome 
99422028508SToomas Soome 		len = strlen(prefix);
99522028508SToomas Soome 		if (len < pathlen && memcmp(path, prefix, len) == 0) {
99622028508SToomas Soome 			path += len;
99722028508SToomas Soome 			pathlen -= len;
99822028508SToomas Soome 		}
99922028508SToomas Soome 		name = malloc(pathlen + 1);
100022028508SToomas Soome 		if (name != NULL) {
100122028508SToomas Soome 			bcopy(path, name, pathlen);
100222028508SToomas Soome 			name[pathlen] = '\0';
100322028508SToomas Soome 		}
100422028508SToomas Soome 		vdev->v_name = name;
100522028508SToomas Soome 		vdev->v_phys_path = NULL;
100622028508SToomas Soome 		vdev->v_devid = NULL;
100722028508SToomas Soome 		if (nvlist_find(nvlist, ZPOOL_CONFIG_PHYS_PATH,
100822028508SToomas Soome 		    DATA_TYPE_STRING, NULL, &path, &pathlen) == 0) {
100922028508SToomas Soome 			name = malloc(pathlen + 1);
101022028508SToomas Soome 			if (name != NULL) {
101122028508SToomas Soome 				bcopy(path, name, pathlen);
101222028508SToomas Soome 				name[pathlen] = '\0';
101322028508SToomas Soome 				vdev->v_phys_path = name;
101422028508SToomas Soome 			}
101522028508SToomas Soome 		}
101622028508SToomas Soome 		if (nvlist_find(nvlist, ZPOOL_CONFIG_DEVID,
101722028508SToomas Soome 		    DATA_TYPE_STRING, NULL, &path, &pathlen) == 0) {
101822028508SToomas Soome 			name = malloc(pathlen + 1);
101922028508SToomas Soome 			if (name != NULL) {
102022028508SToomas Soome 				bcopy(path, name, pathlen);
102122028508SToomas Soome 				name[pathlen] = '\0';
102222028508SToomas Soome 				vdev->v_devid = name;
102322028508SToomas Soome 			}
102422028508SToomas Soome 		}
102522028508SToomas Soome 	} else {
102622028508SToomas Soome 		name = NULL;
102722028508SToomas Soome 		if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0) {
102822028508SToomas Soome 			if (vdev->v_nparity < 1 ||
102922028508SToomas Soome 			    vdev->v_nparity > 3) {
103022028508SToomas Soome 				printf("ZFS: invalid raidz parity: %d\n",
103122028508SToomas Soome 				    vdev->v_nparity);
103222028508SToomas Soome 				return (EIO);
103322028508SToomas Soome 			}
103422028508SToomas Soome 			(void) asprintf(&name, "%.*s%d-%" PRIu64, len, type,
103522028508SToomas Soome 			    vdev->v_nparity, id);
103622028508SToomas Soome 		} else {
103722028508SToomas Soome 			(void) asprintf(&name, "%.*s-%" PRIu64, len, type, id);
103822028508SToomas Soome 		}
103922028508SToomas Soome 		vdev->v_name = name;
104022028508SToomas Soome 	}
104122028508SToomas Soome 	*vdevp = vdev;
104222028508SToomas Soome 	return (0);
104322028508SToomas Soome }
104422028508SToomas Soome 
104522028508SToomas Soome /*
104622028508SToomas Soome  * Find slot for vdev. We return either NULL to signal to use
104722028508SToomas Soome  * STAILQ_INSERT_HEAD, or we return link element to be used with
104822028508SToomas Soome  * STAILQ_INSERT_AFTER.
104922028508SToomas Soome  */
105022028508SToomas Soome static vdev_t *
vdev_find_previous(vdev_t * top_vdev,vdev_t * vdev)105122028508SToomas Soome vdev_find_previous(vdev_t *top_vdev, vdev_t *vdev)
105222028508SToomas Soome {
105322028508SToomas Soome 	vdev_t *v, *previous;
105422028508SToomas Soome 
105522028508SToomas Soome 	if (STAILQ_EMPTY(&top_vdev->v_children))
105622028508SToomas Soome 		return (NULL);
105722028508SToomas Soome 
105822028508SToomas Soome 	previous = NULL;
105922028508SToomas Soome 	STAILQ_FOREACH(v, &top_vdev->v_children, v_childlink) {
106022028508SToomas Soome 		if (v->v_id > vdev->v_id)
106122028508SToomas Soome 			return (previous);
106222028508SToomas Soome 
106322028508SToomas Soome 		if (v->v_id == vdev->v_id)
106422028508SToomas Soome 			return (v);
106522028508SToomas Soome 
106622028508SToomas Soome 		if (v->v_id < vdev->v_id)
106722028508SToomas Soome 			previous = v;
106822028508SToomas Soome 	}
106922028508SToomas Soome 	return (previous);
107022028508SToomas Soome }
107122028508SToomas Soome 
107222028508SToomas Soome static size_t
vdev_child_count(vdev_t * vdev)107322028508SToomas Soome vdev_child_count(vdev_t *vdev)
107422028508SToomas Soome {
107522028508SToomas Soome 	vdev_t *v;
107622028508SToomas Soome 	size_t count;
107722028508SToomas Soome 
107822028508SToomas Soome 	count = 0;
107922028508SToomas Soome 	STAILQ_FOREACH(v, &vdev->v_children, v_childlink) {
108022028508SToomas Soome 		count++;
108122028508SToomas Soome 	}
108222028508SToomas Soome 	return (count);
108322028508SToomas Soome }
108422028508SToomas Soome 
108522028508SToomas Soome /*
108622028508SToomas Soome  * Insert vdev into top_vdev children list. List is ordered by v_id.
108722028508SToomas Soome  */
108822028508SToomas Soome static void
vdev_insert(vdev_t * top_vdev,vdev_t * vdev)108922028508SToomas Soome vdev_insert(vdev_t *top_vdev, vdev_t *vdev)
109022028508SToomas Soome {
109122028508SToomas Soome 	vdev_t *previous;
109222028508SToomas Soome 	size_t count;
109322028508SToomas Soome 
109422028508SToomas Soome 	/*
109522028508SToomas Soome 	 * The top level vdev can appear in random order, depending how
109622028508SToomas Soome 	 * the firmware is presenting the disk devices.
109722028508SToomas Soome 	 * However, we will insert vdev to create list ordered by v_id,
109822028508SToomas Soome 	 * so we can use either STAILQ_INSERT_HEAD or STAILQ_INSERT_AFTER
109922028508SToomas Soome 	 * as STAILQ does not have insert before.
110022028508SToomas Soome 	 */
110122028508SToomas Soome 	previous = vdev_find_previous(top_vdev, vdev);
110222028508SToomas Soome 
110322028508SToomas Soome 	if (previous == NULL) {
110422028508SToomas Soome 		STAILQ_INSERT_HEAD(&top_vdev->v_children, vdev, v_childlink);
110522028508SToomas Soome 	} else if (previous->v_id == vdev->v_id) {
110622028508SToomas Soome 		/*
110722028508SToomas Soome 		 * This vdev was configured from label config,
110822028508SToomas Soome 		 * do not insert duplicate.
110922028508SToomas Soome 		 */
111022028508SToomas Soome 		return;
111122028508SToomas Soome 	} else {
111222028508SToomas Soome 		STAILQ_INSERT_AFTER(&top_vdev->v_children, previous, vdev,
111322028508SToomas Soome 		    v_childlink);
111422028508SToomas Soome 	}
111522028508SToomas Soome 
111622028508SToomas Soome 	count = vdev_child_count(top_vdev);
111722028508SToomas Soome 	if (top_vdev->v_nchildren < count)
111822028508SToomas Soome 		top_vdev->v_nchildren = count;
111922028508SToomas Soome }
112022028508SToomas Soome 
112122028508SToomas Soome static int
vdev_from_nvlist(spa_t * spa,uint64_t top_guid,const nvlist_t * nvlist)112222028508SToomas Soome vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const nvlist_t *nvlist)
112322028508SToomas Soome {
112422028508SToomas Soome 	vdev_t *top_vdev, *vdev;
112522028508SToomas Soome 	nvlist_t **kids = NULL;
112622028508SToomas Soome 	int rc, nkids;
112722028508SToomas Soome 
112822028508SToomas Soome 	/* Get top vdev. */
112922028508SToomas Soome 	top_vdev = vdev_find(top_guid);
113022028508SToomas Soome 	if (top_vdev == NULL) {
113122028508SToomas Soome 		rc = vdev_init(top_guid, nvlist, &top_vdev);
113222028508SToomas Soome 		if (rc != 0)
113322028508SToomas Soome 			return (rc);
113422028508SToomas Soome 		top_vdev->v_spa = spa;
113522028508SToomas Soome 		top_vdev->v_top = top_vdev;
113622028508SToomas Soome 		vdev_insert(spa->spa_root_vdev, top_vdev);
113722028508SToomas Soome 	}
113822028508SToomas Soome 
113922028508SToomas Soome 	/* Add children if there are any. */
114022028508SToomas Soome 	rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
114122028508SToomas Soome 	    &nkids, &kids, NULL);
114222028508SToomas Soome 	if (rc == 0) {
114322028508SToomas Soome 		for (int i = 0; i < nkids; i++) {
114422028508SToomas Soome 			uint64_t guid;
114522028508SToomas Soome 
114622028508SToomas Soome 			rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID,
114722028508SToomas Soome 			    DATA_TYPE_UINT64, NULL, &guid, NULL);
114822028508SToomas Soome 			if (rc != 0)
114922028508SToomas Soome 				goto done;
115022028508SToomas Soome 
115122028508SToomas Soome 			rc = vdev_init(guid, kids[i], &vdev);
115222028508SToomas Soome 			if (rc != 0)
115322028508SToomas Soome 				goto done;
115422028508SToomas Soome 
115522028508SToomas Soome 			vdev->v_spa = spa;
115622028508SToomas Soome 			vdev->v_top = top_vdev;
115722028508SToomas Soome 			vdev_insert(top_vdev, vdev);
115822028508SToomas Soome 		}
115922028508SToomas Soome 	} else {
116022028508SToomas Soome 		/*
116122028508SToomas Soome 		 * When there are no children, nvlist_find() does return
116222028508SToomas Soome 		 * error, reset it because leaf devices have no children.
116322028508SToomas Soome 		 */
116422028508SToomas Soome 		rc = 0;
116522028508SToomas Soome 	}
116622028508SToomas Soome done:
116722028508SToomas Soome 	if (kids != NULL) {
116822028508SToomas Soome 		for (int i = 0; i < nkids; i++)
116922028508SToomas Soome 			nvlist_destroy(kids[i]);
117022028508SToomas Soome 		free(kids);
117122028508SToomas Soome 	}
117222028508SToomas Soome 
117322028508SToomas Soome 	return (rc);
117422028508SToomas Soome }
117522028508SToomas Soome 
117622028508SToomas Soome static int
vdev_init_from_label(spa_t * spa,const nvlist_t * nvlist)117722028508SToomas Soome vdev_init_from_label(spa_t *spa, const nvlist_t *nvlist)
117822028508SToomas Soome {
117922028508SToomas Soome 	uint64_t pool_guid, top_guid;
118022028508SToomas Soome 	nvlist_t *vdevs;
118122028508SToomas Soome 	int rc;
118222028508SToomas Soome 
118322028508SToomas Soome 	if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
118422028508SToomas Soome 	    NULL, &pool_guid, NULL) ||
118522028508SToomas Soome 	    nvlist_find(nvlist, ZPOOL_CONFIG_TOP_GUID, DATA_TYPE_UINT64,
118622028508SToomas Soome 	    NULL, &top_guid, NULL) ||
118722028508SToomas Soome 	    nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
118822028508SToomas Soome 	    NULL, &vdevs, NULL)) {
118922028508SToomas Soome 		printf("ZFS: can't find vdev details\n");
119022028508SToomas Soome 		return (ENOENT);
119122028508SToomas Soome 	}
119222028508SToomas Soome 
119322028508SToomas Soome 	rc = vdev_from_nvlist(spa, top_guid, vdevs);
119422028508SToomas Soome 	nvlist_destroy(vdevs);
119522028508SToomas Soome 	return (rc);
119622028508SToomas Soome }
119722028508SToomas Soome 
119822028508SToomas Soome static void
vdev_set_state(vdev_t * vdev)119922028508SToomas Soome vdev_set_state(vdev_t *vdev)
120022028508SToomas Soome {
120122028508SToomas Soome 	vdev_t *kid;
120222028508SToomas Soome 	int good_kids;
120322028508SToomas Soome 	int bad_kids;
120422028508SToomas Soome 
120522028508SToomas Soome 	STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
120622028508SToomas Soome 		vdev_set_state(kid);
120722028508SToomas Soome 	}
120822028508SToomas Soome 
120922028508SToomas Soome 	/*
121022028508SToomas Soome 	 * A mirror or raidz is healthy if all its kids are healthy. A
121122028508SToomas Soome 	 * mirror is degraded if any of its kids is healthy; a raidz
121222028508SToomas Soome 	 * is degraded if at most nparity kids are offline.
121322028508SToomas Soome 	 */
121422028508SToomas Soome 	if (STAILQ_FIRST(&vdev->v_children)) {
121522028508SToomas Soome 		good_kids = 0;
121622028508SToomas Soome 		bad_kids = 0;
121722028508SToomas Soome 		STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
121822028508SToomas Soome 			if (kid->v_state == VDEV_STATE_HEALTHY)
121922028508SToomas Soome 				good_kids++;
122022028508SToomas Soome 			else
122122028508SToomas Soome 				bad_kids++;
122222028508SToomas Soome 		}
122322028508SToomas Soome 		if (bad_kids == 0) {
122422028508SToomas Soome 			vdev->v_state = VDEV_STATE_HEALTHY;
122522028508SToomas Soome 		} else {
122622028508SToomas Soome 			if (vdev->v_read == vdev_mirror_read) {
122722028508SToomas Soome 				if (good_kids) {
122822028508SToomas Soome 					vdev->v_state = VDEV_STATE_DEGRADED;
122922028508SToomas Soome 				} else {
123022028508SToomas Soome 					vdev->v_state = VDEV_STATE_OFFLINE;
123122028508SToomas Soome 				}
123222028508SToomas Soome 			} else if (vdev->v_read == vdev_raidz_read) {
123322028508SToomas Soome 				if (bad_kids > vdev->v_nparity) {
123422028508SToomas Soome 					vdev->v_state = VDEV_STATE_OFFLINE;
123522028508SToomas Soome 				} else {
123622028508SToomas Soome 					vdev->v_state = VDEV_STATE_DEGRADED;
123722028508SToomas Soome 				}
123822028508SToomas Soome 			}
123922028508SToomas Soome 		}
124022028508SToomas Soome 	}
124122028508SToomas Soome }
124222028508SToomas Soome 
124322028508SToomas Soome static int
vdev_update_from_nvlist(uint64_t top_guid,const nvlist_t * nvlist)124422028508SToomas Soome vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist)
124522028508SToomas Soome {
124622028508SToomas Soome 	vdev_t *vdev;
124722028508SToomas Soome 	nvlist_t **kids = NULL;
124822028508SToomas Soome 	int rc, nkids;
124922028508SToomas Soome 
125022028508SToomas Soome 	/* Update top vdev. */
125122028508SToomas Soome 	vdev = vdev_find(top_guid);
125222028508SToomas Soome 	if (vdev != NULL)
125322028508SToomas Soome 		vdev_set_initial_state(vdev, nvlist);
125422028508SToomas Soome 
125522028508SToomas Soome 	/* Update children if there are any. */
125622028508SToomas Soome 	rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
125722028508SToomas Soome 	    &nkids, &kids, NULL);
125822028508SToomas Soome 	if (rc == 0) {
125922028508SToomas Soome 		for (int i = 0; i < nkids; i++) {
126022028508SToomas Soome 			uint64_t guid;
126122028508SToomas Soome 
126222028508SToomas Soome 			rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID,
126322028508SToomas Soome 			    DATA_TYPE_UINT64, NULL, &guid, NULL);
126422028508SToomas Soome 			if (rc != 0)
126522028508SToomas Soome 				break;
126622028508SToomas Soome 
126722028508SToomas Soome 			vdev = vdev_find(guid);
126822028508SToomas Soome 			if (vdev != NULL)
126922028508SToomas Soome 				vdev_set_initial_state(vdev, kids[i]);
127022028508SToomas Soome 		}
127122028508SToomas Soome 	} else {
127222028508SToomas Soome 		rc = 0;
127322028508SToomas Soome 	}
127422028508SToomas Soome 	if (kids != NULL) {
127522028508SToomas Soome 		for (int i = 0; i < nkids; i++)
127622028508SToomas Soome 			nvlist_destroy(kids[i]);
127722028508SToomas Soome 		free(kids);
127822028508SToomas Soome 	}
127922028508SToomas Soome 
128022028508SToomas Soome 	return (rc);
128122028508SToomas Soome }
128222028508SToomas Soome 
128322028508SToomas Soome static int
vdev_init_from_nvlist(spa_t * spa,const nvlist_t * nvlist)128422028508SToomas Soome vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist)
128522028508SToomas Soome {
128622028508SToomas Soome 	uint64_t pool_guid, vdev_children;
128722028508SToomas Soome 	nvlist_t *vdevs = NULL, **kids = NULL;
128822028508SToomas Soome 	int rc, nkids;
128922028508SToomas Soome 
129022028508SToomas Soome 	if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
129122028508SToomas Soome 	    NULL, &pool_guid, NULL) ||
129222028508SToomas Soome 	    nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_CHILDREN, DATA_TYPE_UINT64,
129322028508SToomas Soome 	    NULL, &vdev_children, NULL) ||
129422028508SToomas Soome 	    nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
129522028508SToomas Soome 	    NULL, &vdevs, NULL)) {
129622028508SToomas Soome 		printf("ZFS: can't find vdev details\n");
129722028508SToomas Soome 		return (ENOENT);
129822028508SToomas Soome 	}
129922028508SToomas Soome 
130022028508SToomas Soome 	/* Wrong guid?! */
130122028508SToomas Soome 	if (spa->spa_guid != pool_guid) {
130222028508SToomas Soome 		nvlist_destroy(vdevs);
130322028508SToomas Soome 		return (EINVAL);
130422028508SToomas Soome 	}
130522028508SToomas Soome 
130622028508SToomas Soome 	spa->spa_root_vdev->v_nchildren = vdev_children;
130722028508SToomas Soome 
130822028508SToomas Soome 	rc = nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
130922028508SToomas Soome 	    &nkids, &kids, NULL);
131022028508SToomas Soome 	nvlist_destroy(vdevs);
131122028508SToomas Soome 
131222028508SToomas Soome 	/*
131322028508SToomas Soome 	 * MOS config has at least one child for root vdev.
131422028508SToomas Soome 	 */
131522028508SToomas Soome 	if (rc != 0)
131622028508SToomas Soome 		return (rc);
131722028508SToomas Soome 
131822028508SToomas Soome 	for (int i = 0; i < nkids; i++) {
131922028508SToomas Soome 		uint64_t guid;
132022028508SToomas Soome 		vdev_t *vdev;
132122028508SToomas Soome 
132222028508SToomas Soome 		rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
132322028508SToomas Soome 		    NULL, &guid, NULL);
132422028508SToomas Soome 		if (rc != 0)
132522028508SToomas Soome 			break;
132622028508SToomas Soome 		vdev = vdev_find(guid);
132722028508SToomas Soome 		/*
132822028508SToomas Soome 		 * Top level vdev is missing, create it.
132922028508SToomas Soome 		 */
133022028508SToomas Soome 		if (vdev == NULL)
133122028508SToomas Soome 			rc = vdev_from_nvlist(spa, guid, kids[i]);
133222028508SToomas Soome 		else
133322028508SToomas Soome 			rc = vdev_update_from_nvlist(guid, kids[i]);
133422028508SToomas Soome 		if (rc != 0)
133522028508SToomas Soome 			break;
133622028508SToomas Soome 	}
133722028508SToomas Soome 	if (kids != NULL) {
133822028508SToomas Soome 		for (int i = 0; i < nkids; i++)
133922028508SToomas Soome 			nvlist_destroy(kids[i]);
134022028508SToomas Soome 		free(kids);
134122028508SToomas Soome 	}
134222028508SToomas Soome 
134322028508SToomas Soome 	/*
134422028508SToomas Soome 	 * Re-evaluate top-level vdev state.
134522028508SToomas Soome 	 */
134622028508SToomas Soome 	vdev_set_state(spa->spa_root_vdev);
134722028508SToomas Soome 
134822028508SToomas Soome 	return (rc);
134922028508SToomas Soome }
135022028508SToomas Soome 
135122028508SToomas Soome static spa_t *
spa_find_by_guid(uint64_t guid)135222028508SToomas Soome spa_find_by_guid(uint64_t guid)
135322028508SToomas Soome {
135422028508SToomas Soome 	spa_t *spa;
135522028508SToomas Soome 
135622028508SToomas Soome 	STAILQ_FOREACH(spa, &zfs_pools, spa_link)
135722028508SToomas Soome 		if (spa->spa_guid == guid)
135822028508SToomas Soome 			return (spa);
135922028508SToomas Soome 
136022028508SToomas Soome 	return (NULL);
136122028508SToomas Soome }
136222028508SToomas Soome 
136322028508SToomas Soome static spa_t *
spa_find_by_name(const char * name)136422028508SToomas Soome spa_find_by_name(const char *name)
136522028508SToomas Soome {
136622028508SToomas Soome 	spa_t *spa;
136722028508SToomas Soome 
136822028508SToomas Soome 	STAILQ_FOREACH(spa, &zfs_pools, spa_link)
136922028508SToomas Soome 		if (strcmp(spa->spa_name, name) == 0)
137022028508SToomas Soome 			return (spa);
137122028508SToomas Soome 
137222028508SToomas Soome 	return (NULL);
137322028508SToomas Soome }
137422028508SToomas Soome 
137522028508SToomas Soome static spa_t *
spa_find_by_dev(struct zfs_devdesc * dev)137622028508SToomas Soome spa_find_by_dev(struct zfs_devdesc *dev)
137722028508SToomas Soome {
137822028508SToomas Soome 
137922028508SToomas Soome 	if (dev->dd.d_dev->dv_type != DEVT_ZFS)
138022028508SToomas Soome 		return (NULL);
138122028508SToomas Soome 
138222028508SToomas Soome 	if (dev->pool_guid == 0)
138322028508SToomas Soome 		return (STAILQ_FIRST(&zfs_pools));
138422028508SToomas Soome 
138522028508SToomas Soome 	return (spa_find_by_guid(dev->pool_guid));
138622028508SToomas Soome }
138722028508SToomas Soome 
138822028508SToomas Soome static spa_t *
spa_create(uint64_t guid,const char * name)138922028508SToomas Soome spa_create(uint64_t guid, const char *name)
139022028508SToomas Soome {
139122028508SToomas Soome 	spa_t *spa;
139222028508SToomas Soome 
139322028508SToomas Soome 	if ((spa = calloc(1, sizeof (spa_t))) == NULL)
139422028508SToomas Soome 		return (NULL);
139522028508SToomas Soome 	if ((spa->spa_name = strdup(name)) == NULL) {
139622028508SToomas Soome 		free(spa);
139722028508SToomas Soome 		return (NULL);
139822028508SToomas Soome 	}
139922028508SToomas Soome 	spa->spa_guid = guid;
140022028508SToomas Soome 	spa->spa_root_vdev = vdev_create(guid, NULL);
140122028508SToomas Soome 	if (spa->spa_root_vdev == NULL) {
140222028508SToomas Soome 		free(spa->spa_name);
140322028508SToomas Soome 		free(spa);
140422028508SToomas Soome 		return (NULL);
140522028508SToomas Soome 	}
140622028508SToomas Soome 	spa->spa_root_vdev->v_name = strdup("root");
140722028508SToomas Soome 	STAILQ_INSERT_TAIL(&zfs_pools, spa, spa_link);
140822028508SToomas Soome 
140922028508SToomas Soome 	return (spa);
141022028508SToomas Soome }
141122028508SToomas Soome 
141222028508SToomas Soome static const char *
state_name(vdev_state_t state)141322028508SToomas Soome state_name(vdev_state_t state)
141422028508SToomas Soome {
141522028508SToomas Soome 	static const char *names[] = {
141622028508SToomas Soome 		"UNKNOWN",
141722028508SToomas Soome 		"CLOSED",
141822028508SToomas Soome 		"OFFLINE",
141922028508SToomas Soome 		"REMOVED",
142022028508SToomas Soome 		"CANT_OPEN",
142122028508SToomas Soome 		"FAULTED",
142222028508SToomas Soome 		"DEGRADED",
142322028508SToomas Soome 		"ONLINE"
142422028508SToomas Soome 	};
142522028508SToomas Soome 	return (names[state]);
142622028508SToomas Soome }
142722028508SToomas Soome 
142822028508SToomas Soome static int
pager_printf(const char * fmt,...)142922028508SToomas Soome pager_printf(const char *fmt, ...)
143022028508SToomas Soome {
143122028508SToomas Soome 	char line[80];
143222028508SToomas Soome 	va_list args;
143322028508SToomas Soome 
143422028508SToomas Soome 	va_start(args, fmt);
143522028508SToomas Soome 	vsnprintf(line, sizeof (line), fmt, args);
143622028508SToomas Soome 	va_end(args);
143722028508SToomas Soome 	return (pager_output(line));
143822028508SToomas Soome }
143922028508SToomas Soome 
144022028508SToomas Soome #define	STATUS_FORMAT	"        %s %s\n"
144122028508SToomas Soome 
144222028508SToomas Soome static int
print_state(int indent,const char * name,vdev_state_t state)144322028508SToomas Soome print_state(int indent, const char *name, vdev_state_t state)
144422028508SToomas Soome {
144522028508SToomas Soome 	int i;
144622028508SToomas Soome 	char buf[512];
144722028508SToomas Soome 
144822028508SToomas Soome 	buf[0] = 0;
144922028508SToomas Soome 	for (i = 0; i < indent; i++)
145022028508SToomas Soome 		strcat(buf, "  ");
145122028508SToomas Soome 	strcat(buf, name);
145222028508SToomas Soome 	return (pager_printf(STATUS_FORMAT, buf, state_name(state)));
145322028508SToomas Soome }
145422028508SToomas Soome 
145522028508SToomas Soome static int
vdev_status(vdev_t * vdev,int indent)145622028508SToomas Soome vdev_status(vdev_t *vdev, int indent)
145722028508SToomas Soome {
145822028508SToomas Soome 	vdev_t *kid;
145922028508SToomas Soome 	int ret;
146022028508SToomas Soome 
146122028508SToomas Soome 	if (vdev->v_islog) {
146222028508SToomas Soome 		(void) pager_output("        logs\n");
146322028508SToomas Soome 		indent++;
146422028508SToomas Soome 	}
146522028508SToomas Soome 
146622028508SToomas Soome 	ret = print_state(indent, vdev->v_name, vdev->v_state);
146722028508SToomas Soome 	if (ret != 0)
146822028508SToomas Soome 		return (ret);
146922028508SToomas Soome 
147022028508SToomas Soome 	STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
147122028508SToomas Soome 		ret = vdev_status(kid, indent + 1);
147222028508SToomas Soome 		if (ret != 0)
147322028508SToomas Soome 			return (ret);
147422028508SToomas Soome 	}
147522028508SToomas Soome 	return (ret);
147622028508SToomas Soome }
147722028508SToomas Soome 
147822028508SToomas Soome static int
spa_status(spa_t * spa)147922028508SToomas Soome spa_status(spa_t *spa)
148022028508SToomas Soome {
148122028508SToomas Soome 	static char bootfs[ZFS_MAXNAMELEN];
148222028508SToomas Soome 	uint64_t rootid;
148322028508SToomas Soome 	vdev_list_t *vlist;
148422028508SToomas Soome 	vdev_t *vdev;
148522028508SToomas Soome 	int good_kids, bad_kids, degraded_kids, ret;
148622028508SToomas Soome 	vdev_state_t state;
148722028508SToomas Soome 
148822028508SToomas Soome 	ret = pager_printf("  pool: %s\n", spa->spa_name);
148922028508SToomas Soome 	if (ret != 0)
149022028508SToomas Soome 		return (ret);
149122028508SToomas Soome 
149222028508SToomas Soome 	if (zfs_get_root(spa, &rootid) == 0 &&
149322028508SToomas Soome 	    zfs_rlookup(spa, rootid, bootfs) == 0) {
149422028508SToomas Soome 		if (bootfs[0] == '\0')
149522028508SToomas Soome 			ret = pager_printf("bootfs: %s\n", spa->spa_name);
149622028508SToomas Soome 		else
149722028508SToomas Soome 			ret = pager_printf("bootfs: %s/%s\n", spa->spa_name,
149822028508SToomas Soome 			    bootfs);
149922028508SToomas Soome 		if (ret != 0)
150022028508SToomas Soome 			return (ret);
150122028508SToomas Soome 	}
150222028508SToomas Soome 	ret = pager_printf("config:\n\n");
150322028508SToomas Soome 	if (ret != 0)
150422028508SToomas Soome 		return (ret);
150522028508SToomas Soome 	ret = pager_printf(STATUS_FORMAT, "NAME", "STATE");
150622028508SToomas Soome 	if (ret != 0)
150722028508SToomas Soome 		return (ret);
150822028508SToomas Soome 
150922028508SToomas Soome 	good_kids = 0;
151022028508SToomas Soome 	degraded_kids = 0;
151122028508SToomas Soome 	bad_kids = 0;
151222028508SToomas Soome 	vlist = &spa->spa_root_vdev->v_children;
151322028508SToomas Soome 	STAILQ_FOREACH(vdev, vlist, v_childlink) {
151422028508SToomas Soome 		if (vdev->v_state == VDEV_STATE_HEALTHY)
151522028508SToomas Soome 			good_kids++;
151622028508SToomas Soome 		else if (vdev->v_state == VDEV_STATE_DEGRADED)
151722028508SToomas Soome 			degraded_kids++;
151822028508SToomas Soome 		else
151922028508SToomas Soome 			bad_kids++;
152022028508SToomas Soome 	}
152122028508SToomas Soome 
152222028508SToomas Soome 	state = VDEV_STATE_CLOSED;
152322028508SToomas Soome 	if (good_kids > 0 && (degraded_kids + bad_kids) == 0)
152422028508SToomas Soome 		state = VDEV_STATE_HEALTHY;
152522028508SToomas Soome 	else if ((good_kids + degraded_kids) > 0)
152622028508SToomas Soome 		state = VDEV_STATE_DEGRADED;
152722028508SToomas Soome 
152822028508SToomas Soome 	ret = print_state(0, spa->spa_name, state);
152922028508SToomas Soome 	if (ret != 0)
153022028508SToomas Soome 		return (ret);
153122028508SToomas Soome 
153222028508SToomas Soome 	STAILQ_FOREACH(vdev, vlist, v_childlink) {
153322028508SToomas Soome 		ret = vdev_status(vdev, 1);
153422028508SToomas Soome 		if (ret != 0)
153522028508SToomas Soome 			return (ret);
153622028508SToomas Soome 	}
153722028508SToomas Soome 	return (ret);
153822028508SToomas Soome }
153922028508SToomas Soome 
154022028508SToomas Soome int
spa_all_status(void)154122028508SToomas Soome spa_all_status(void)
154222028508SToomas Soome {
154322028508SToomas Soome 	spa_t *spa;
154422028508SToomas Soome 	int first = 1, ret = 0;
154522028508SToomas Soome 
154622028508SToomas Soome 	STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
154722028508SToomas Soome 		if (!first) {
154822028508SToomas Soome 			ret = pager_printf("\n");
154922028508SToomas Soome 			if (ret != 0)
155022028508SToomas Soome 				return (ret);
155122028508SToomas Soome 		}
155222028508SToomas Soome 		first = 0;
155322028508SToomas Soome 		ret = spa_status(spa);
155422028508SToomas Soome 		if (ret != 0)
155522028508SToomas Soome 			return (ret);
155622028508SToomas Soome 	}
155722028508SToomas Soome 	return (ret);
155822028508SToomas Soome }
155922028508SToomas Soome 
156022028508SToomas Soome uint64_t
vdev_label_offset(uint64_t psize,int l,uint64_t offset)156122028508SToomas Soome vdev_label_offset(uint64_t psize, int l, uint64_t offset)
156222028508SToomas Soome {
156322028508SToomas Soome 	uint64_t label_offset;
156422028508SToomas Soome 
156522028508SToomas Soome 	if (l < VDEV_LABELS / 2)
156622028508SToomas Soome 		label_offset = 0;
156722028508SToomas Soome 	else
156822028508SToomas Soome 		label_offset = psize - VDEV_LABELS * sizeof (vdev_label_t);
156922028508SToomas Soome 
157022028508SToomas Soome 	return (offset + l * sizeof (vdev_label_t) + label_offset);
157122028508SToomas Soome }
157222028508SToomas Soome 
157322028508SToomas Soome static int
vdev_uberblock_compare(const uberblock_t * ub1,const uberblock_t * ub2)157422028508SToomas Soome vdev_uberblock_compare(const uberblock_t *ub1, const uberblock_t *ub2)
157522028508SToomas Soome {
157622028508SToomas Soome 	unsigned int seq1 = 0;
157722028508SToomas Soome 	unsigned int seq2 = 0;
157822028508SToomas Soome 	int cmp = AVL_CMP(ub1->ub_txg, ub2->ub_txg);
157922028508SToomas Soome 
158022028508SToomas Soome 	if (cmp != 0)
158122028508SToomas Soome 		return (cmp);
158222028508SToomas Soome 
158322028508SToomas Soome 	cmp = AVL_CMP(ub1->ub_timestamp, ub2->ub_timestamp);
158422028508SToomas Soome 	if (cmp != 0)
158522028508SToomas Soome 		return (cmp);
158622028508SToomas Soome 
158722028508SToomas Soome 	if (MMP_VALID(ub1) && MMP_SEQ_VALID(ub1))
158822028508SToomas Soome 		seq1 = MMP_SEQ(ub1);
158922028508SToomas Soome 
159022028508SToomas Soome 	if (MMP_VALID(ub2) && MMP_SEQ_VALID(ub2))
159122028508SToomas Soome 		seq2 = MMP_SEQ(ub2);
159222028508SToomas Soome 
159322028508SToomas Soome 	return (AVL_CMP(seq1, seq2));
159422028508SToomas Soome }
159522028508SToomas Soome 
159622028508SToomas Soome static int
uberblock_verify(uberblock_t * ub)159722028508SToomas Soome uberblock_verify(uberblock_t *ub)
159822028508SToomas Soome {
159922028508SToomas Soome 	if (ub->ub_magic == BSWAP_64((uint64_t)UBERBLOCK_MAGIC)) {
160022028508SToomas Soome 		byteswap_uint64_array(ub, sizeof (uberblock_t));
160122028508SToomas Soome 	}
160222028508SToomas Soome 
160322028508SToomas Soome 	if (ub->ub_magic != UBERBLOCK_MAGIC ||
160422028508SToomas Soome 	    !SPA_VERSION_IS_SUPPORTED(ub->ub_version))
160522028508SToomas Soome 		return (EINVAL);
160622028508SToomas Soome 
160722028508SToomas Soome 	return (0);
160822028508SToomas Soome }
160922028508SToomas Soome 
161022028508SToomas Soome static int
vdev_label_read(vdev_t * vd,int l,void * buf,uint64_t offset,size_t size)161122028508SToomas Soome vdev_label_read(vdev_t *vd, int l, void *buf, uint64_t offset,
161222028508SToomas Soome     size_t size)
161322028508SToomas Soome {
161422028508SToomas Soome 	blkptr_t bp;
161522028508SToomas Soome 	off_t off;
161622028508SToomas Soome 
161722028508SToomas Soome 	off = vdev_label_offset(vd->v_psize, l, offset);
161822028508SToomas Soome 
161922028508SToomas Soome 	BP_ZERO(&bp);
162022028508SToomas Soome 	BP_SET_LSIZE(&bp, size);
162122028508SToomas Soome 	BP_SET_PSIZE(&bp, size);
162222028508SToomas Soome 	BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
162322028508SToomas Soome 	BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
162422028508SToomas Soome 	DVA_SET_OFFSET(BP_IDENTITY(&bp), off);
162522028508SToomas Soome 	ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
162622028508SToomas Soome 
162722028508SToomas Soome 	return (vdev_read_phys(vd, &bp, buf, off, size));
162822028508SToomas Soome }
162922028508SToomas Soome 
163022028508SToomas Soome /*
163122028508SToomas Soome  * We do need to be sure we write to correct location.
163222028508SToomas Soome  * Our vdev label does consist of 4 fields:
163322028508SToomas Soome  * pad1 (8k), reserved.
163422028508SToomas Soome  * bootenv (8k), checksummed, previously reserved, may contain garbage.
163522028508SToomas Soome  * vdev_phys (112k), checksummed
163622028508SToomas Soome  * uberblock ring (128k), checksummed.
163722028508SToomas Soome  *
163822028508SToomas Soome  * Since bootenv area may contain garbage, we can not reliably read it, as
163922028508SToomas Soome  * we can get checksum errors.
164022028508SToomas Soome  * Next best thing is vdev_phys - it is just after bootenv. It still may
164122028508SToomas Soome  * be corrupted, but in such case we will miss this one write.
164222028508SToomas Soome  */
164322028508SToomas Soome static int
vdev_label_write_validate(vdev_t * vd,int l,uint64_t offset)164422028508SToomas Soome vdev_label_write_validate(vdev_t *vd, int l, uint64_t offset)
164522028508SToomas Soome {
164622028508SToomas Soome 	uint64_t off, o_phys;
164722028508SToomas Soome 	void *buf;
164822028508SToomas Soome 	size_t size = VDEV_PHYS_SIZE;
164922028508SToomas Soome 	int rc;
165022028508SToomas Soome 
165122028508SToomas Soome 	o_phys = offsetof(vdev_label_t, vl_vdev_phys);
165222028508SToomas Soome 	off = vdev_label_offset(vd->v_psize, l, o_phys);
165322028508SToomas Soome 
165422028508SToomas Soome 	/* off should be 8K from bootenv */
165522028508SToomas Soome 	if (vdev_label_offset(vd->v_psize, l, offset) + VDEV_PAD_SIZE != off)
165622028508SToomas Soome 		return (EINVAL);
165722028508SToomas Soome 
165822028508SToomas Soome 	buf = malloc(size);
165922028508SToomas Soome 	if (buf == NULL)
166022028508SToomas Soome 		return (ENOMEM);
166122028508SToomas Soome 
166222028508SToomas Soome 	/* Read vdev_phys */
166322028508SToomas Soome 	rc = vdev_label_read(vd, l, buf, o_phys, size);
166422028508SToomas Soome 	free(buf);
166522028508SToomas Soome 	return (rc);
166622028508SToomas Soome }
166722028508SToomas Soome 
166822028508SToomas Soome static int
vdev_label_write(vdev_t * vd,int l,vdev_boot_envblock_t * be,uint64_t offset)166922028508SToomas Soome vdev_label_write(vdev_t *vd, int l, vdev_boot_envblock_t *be, uint64_t offset)
167022028508SToomas Soome {
167122028508SToomas Soome 	zio_checksum_info_t *ci;
167222028508SToomas Soome 	zio_cksum_t cksum;
167322028508SToomas Soome 	off_t off;
167422028508SToomas Soome 	size_t size = VDEV_PAD_SIZE;
167522028508SToomas Soome 	int rc;
167622028508SToomas Soome 
167722028508SToomas Soome 	if (vd->v_phys_write == NULL)
167822028508SToomas Soome 		return (ENOTSUP);
167922028508SToomas Soome 
168022028508SToomas Soome 	off = vdev_label_offset(vd->v_psize, l, offset);
168122028508SToomas Soome 
168222028508SToomas Soome 	rc = vdev_label_write_validate(vd, l, offset);
168322028508SToomas Soome 	if (rc != 0) {
168422028508SToomas Soome 		return (rc);
168522028508SToomas Soome 	}
168622028508SToomas Soome 
168722028508SToomas Soome 	ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL];
168822028508SToomas Soome 	be->vbe_zbt.zec_magic = ZEC_MAGIC;
168922028508SToomas Soome 	zio_checksum_label_verifier(&be->vbe_zbt.zec_cksum, off);
169022028508SToomas Soome 	ci->ci_func[0](be, size, NULL, &cksum);
169122028508SToomas Soome 	be->vbe_zbt.zec_cksum = cksum;
169222028508SToomas Soome 
169322028508SToomas Soome 	return (vdev_write_phys(vd, be, off, size));
169422028508SToomas Soome }
169522028508SToomas Soome 
169622028508SToomas Soome static int
vdev_write_bootenv_impl(vdev_t * vdev,vdev_boot_envblock_t * be)169722028508SToomas Soome vdev_write_bootenv_impl(vdev_t *vdev, vdev_boot_envblock_t *be)
169822028508SToomas Soome {
169922028508SToomas Soome 	vdev_t *kid;
1700b7a4a577SWarner Losh 	int rv = 0, err;
170122028508SToomas Soome 
170222028508SToomas Soome 	STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
170322028508SToomas Soome 		if (kid->v_state != VDEV_STATE_HEALTHY)
170422028508SToomas Soome 			continue;
1705b7a4a577SWarner Losh 		err = vdev_write_bootenv_impl(kid, be);
1706b7a4a577SWarner Losh 		if (err != 0)
1707b7a4a577SWarner Losh 			rv = err;
170822028508SToomas Soome 	}
170922028508SToomas Soome 
171022028508SToomas Soome 	/*
171122028508SToomas Soome 	 * Non-leaf vdevs do not have v_phys_write.
171222028508SToomas Soome 	 */
171322028508SToomas Soome 	if (vdev->v_phys_write == NULL)
171422028508SToomas Soome 		return (rv);
171522028508SToomas Soome 
171622028508SToomas Soome 	for (int l = 0; l < VDEV_LABELS; l++) {
1717b7a4a577SWarner Losh 		err = vdev_label_write(vdev, l, be,
171822028508SToomas Soome 		    offsetof(vdev_label_t, vl_be));
1719b7a4a577SWarner Losh 		if (err != 0) {
172022028508SToomas Soome 			printf("failed to write bootenv to %s label %d: %d\n",
1721b7a4a577SWarner Losh 			    vdev->v_name ? vdev->v_name : "unknown", l, err);
1722b7a4a577SWarner Losh 			rv = err;
172322028508SToomas Soome 		}
172422028508SToomas Soome 	}
172522028508SToomas Soome 	return (rv);
172622028508SToomas Soome }
172722028508SToomas Soome 
172822028508SToomas Soome int
vdev_write_bootenv(vdev_t * vdev,nvlist_t * nvl)172922028508SToomas Soome vdev_write_bootenv(vdev_t *vdev, nvlist_t *nvl)
173022028508SToomas Soome {
173122028508SToomas Soome 	vdev_boot_envblock_t *be;
173222028508SToomas Soome 	nvlist_t nv, *nvp;
173322028508SToomas Soome 	uint64_t version;
173422028508SToomas Soome 	int rv;
173522028508SToomas Soome 
173622028508SToomas Soome 	if (nvl->nv_size > sizeof (be->vbe_bootenv))
173722028508SToomas Soome 		return (E2BIG);
173822028508SToomas Soome 
173922028508SToomas Soome 	version = VB_RAW;
174022028508SToomas Soome 	nvp = vdev_read_bootenv(vdev);
174122028508SToomas Soome 	if (nvp != NULL) {
174222028508SToomas Soome 		nvlist_find(nvp, BOOTENV_VERSION, DATA_TYPE_UINT64, NULL,
174322028508SToomas Soome 		    &version, NULL);
174422028508SToomas Soome 		nvlist_destroy(nvp);
174522028508SToomas Soome 	}
174622028508SToomas Soome 
174722028508SToomas Soome 	be = calloc(1, sizeof (*be));
174822028508SToomas Soome 	if (be == NULL)
174922028508SToomas Soome 		return (ENOMEM);
175022028508SToomas Soome 
175122028508SToomas Soome 	be->vbe_version = version;
175222028508SToomas Soome 	switch (version) {
175322028508SToomas Soome 	case VB_RAW:
175422028508SToomas Soome 		/*
175522028508SToomas Soome 		 * If there is no envmap, we will just wipe bootenv.
175622028508SToomas Soome 		 */
175722028508SToomas Soome 		nvlist_find(nvl, GRUB_ENVMAP, DATA_TYPE_STRING, NULL,
175822028508SToomas Soome 		    be->vbe_bootenv, NULL);
175922028508SToomas Soome 		rv = 0;
176022028508SToomas Soome 		break;
176122028508SToomas Soome 
176222028508SToomas Soome 	case VB_NVLIST:
176322028508SToomas Soome 		nv.nv_header = nvl->nv_header;
176422028508SToomas Soome 		nv.nv_asize = nvl->nv_asize;
176522028508SToomas Soome 		nv.nv_size = nvl->nv_size;
176622028508SToomas Soome 
176722028508SToomas Soome 		bcopy(&nv.nv_header, be->vbe_bootenv, sizeof (nv.nv_header));
176822028508SToomas Soome 		nv.nv_data = (uint8_t *)be->vbe_bootenv + sizeof (nvs_header_t);
176922028508SToomas Soome 		bcopy(nvl->nv_data, nv.nv_data, nv.nv_size);
177022028508SToomas Soome 		rv = nvlist_export(&nv);
177122028508SToomas Soome 		break;
177222028508SToomas Soome 
177322028508SToomas Soome 	default:
177422028508SToomas Soome 		rv = EINVAL;
177522028508SToomas Soome 		break;
177622028508SToomas Soome 	}
177722028508SToomas Soome 
177822028508SToomas Soome 	if (rv == 0) {
177922028508SToomas Soome 		be->vbe_version = htobe64(be->vbe_version);
178022028508SToomas Soome 		rv = vdev_write_bootenv_impl(vdev, be);
178122028508SToomas Soome 	}
178222028508SToomas Soome 	free(be);
178322028508SToomas Soome 	return (rv);
178422028508SToomas Soome }
178522028508SToomas Soome 
178622028508SToomas Soome /*
178722028508SToomas Soome  * Read the bootenv area from pool label, return the nvlist from it.
178822028508SToomas Soome  * We return from first successful read.
178922028508SToomas Soome  */
179022028508SToomas Soome nvlist_t *
vdev_read_bootenv(vdev_t * vdev)179122028508SToomas Soome vdev_read_bootenv(vdev_t *vdev)
179222028508SToomas Soome {
179322028508SToomas Soome 	vdev_t *kid;
179422028508SToomas Soome 	nvlist_t *benv;
179522028508SToomas Soome 	vdev_boot_envblock_t *be;
179622028508SToomas Soome 	char *command;
179722028508SToomas Soome 	bool ok;
179822028508SToomas Soome 	int rv;
179922028508SToomas Soome 
180022028508SToomas Soome 	STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) {
180122028508SToomas Soome 		if (kid->v_state != VDEV_STATE_HEALTHY)
180222028508SToomas Soome 			continue;
180322028508SToomas Soome 
180422028508SToomas Soome 		benv = vdev_read_bootenv(kid);
180522028508SToomas Soome 		if (benv != NULL)
180622028508SToomas Soome 			return (benv);
180722028508SToomas Soome 	}
180822028508SToomas Soome 
180922028508SToomas Soome 	be = malloc(sizeof (*be));
181022028508SToomas Soome 	if (be == NULL)
181122028508SToomas Soome 		return (NULL);
181222028508SToomas Soome 
181322028508SToomas Soome 	rv = 0;
181422028508SToomas Soome 	for (int l = 0; l < VDEV_LABELS; l++) {
181522028508SToomas Soome 		rv = vdev_label_read(vdev, l, be,
181622028508SToomas Soome 		    offsetof(vdev_label_t, vl_be),
181722028508SToomas Soome 		    sizeof (*be));
181822028508SToomas Soome 		if (rv == 0)
181922028508SToomas Soome 			break;
182022028508SToomas Soome 	}
182122028508SToomas Soome 	if (rv != 0) {
182222028508SToomas Soome 		free(be);
182322028508SToomas Soome 		return (NULL);
182422028508SToomas Soome 	}
182522028508SToomas Soome 
182622028508SToomas Soome 	be->vbe_version = be64toh(be->vbe_version);
182722028508SToomas Soome 	switch (be->vbe_version) {
182822028508SToomas Soome 	case VB_RAW:
182922028508SToomas Soome 		/*
183022028508SToomas Soome 		 * if we have textual data in vbe_bootenv, create nvlist
183122028508SToomas Soome 		 * with key "envmap".
183222028508SToomas Soome 		 */
183322028508SToomas Soome 		benv = nvlist_create(NV_UNIQUE_NAME);
183422028508SToomas Soome 		if (benv != NULL) {
183522028508SToomas Soome 			if (*be->vbe_bootenv == '\0') {
183622028508SToomas Soome 				nvlist_add_uint64(benv, BOOTENV_VERSION,
183722028508SToomas Soome 				    VB_NVLIST);
183822028508SToomas Soome 				break;
183922028508SToomas Soome 			}
184022028508SToomas Soome 			nvlist_add_uint64(benv, BOOTENV_VERSION, VB_RAW);
184122028508SToomas Soome 			be->vbe_bootenv[sizeof (be->vbe_bootenv) - 1] = '\0';
184222028508SToomas Soome 			nvlist_add_string(benv, GRUB_ENVMAP, be->vbe_bootenv);
184322028508SToomas Soome 		}
184422028508SToomas Soome 		break;
184522028508SToomas Soome 
184622028508SToomas Soome 	case VB_NVLIST:
184722028508SToomas Soome 		benv = nvlist_import(be->vbe_bootenv, sizeof (be->vbe_bootenv));
184822028508SToomas Soome 		break;
184922028508SToomas Soome 
185022028508SToomas Soome 	default:
185122028508SToomas Soome 		command = (char *)be;
185222028508SToomas Soome 		ok = false;
185322028508SToomas Soome 
185422028508SToomas Soome 		/* Check for legacy zfsbootcfg command string */
185522028508SToomas Soome 		for (int i = 0; command[i] != '\0'; i++) {
185622028508SToomas Soome 			if (iscntrl(command[i])) {
185722028508SToomas Soome 				ok = false;
185822028508SToomas Soome 				break;
185922028508SToomas Soome 			} else {
186022028508SToomas Soome 				ok = true;
186122028508SToomas Soome 			}
186222028508SToomas Soome 		}
186322028508SToomas Soome 		benv = nvlist_create(NV_UNIQUE_NAME);
186422028508SToomas Soome 		if (benv != NULL) {
186522028508SToomas Soome 			if (ok)
186622028508SToomas Soome 				nvlist_add_string(benv, FREEBSD_BOOTONCE,
186722028508SToomas Soome 				    command);
186822028508SToomas Soome 			else
186922028508SToomas Soome 				nvlist_add_uint64(benv, BOOTENV_VERSION,
187022028508SToomas Soome 				    VB_NVLIST);
187122028508SToomas Soome 		}
187222028508SToomas Soome 		break;
187322028508SToomas Soome 	}
187422028508SToomas Soome 	free(be);
187522028508SToomas Soome 	return (benv);
187622028508SToomas Soome }
187722028508SToomas Soome 
187822028508SToomas Soome static uint64_t
vdev_get_label_asize(nvlist_t * nvl)187922028508SToomas Soome vdev_get_label_asize(nvlist_t *nvl)
188022028508SToomas Soome {
188122028508SToomas Soome 	nvlist_t *vdevs;
188222028508SToomas Soome 	uint64_t asize;
188322028508SToomas Soome 	const char *type;
188422028508SToomas Soome 	int len;
188522028508SToomas Soome 
188622028508SToomas Soome 	asize = 0;
188722028508SToomas Soome 	/* Get vdev tree */
188822028508SToomas Soome 	if (nvlist_find(nvl, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
188922028508SToomas Soome 	    NULL, &vdevs, NULL) != 0)
189022028508SToomas Soome 		return (asize);
189122028508SToomas Soome 
189222028508SToomas Soome 	/*
189322028508SToomas Soome 	 * Get vdev type. We will calculate asize for raidz, mirror and disk.
189422028508SToomas Soome 	 * For raidz, the asize is raw size of all children.
189522028508SToomas Soome 	 */
189622028508SToomas Soome 	if (nvlist_find(vdevs, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING,
189722028508SToomas Soome 	    NULL, &type, &len) != 0)
189822028508SToomas Soome 		goto done;
189922028508SToomas Soome 
190022028508SToomas Soome 	if (memcmp(type, VDEV_TYPE_MIRROR, len) != 0 &&
190122028508SToomas Soome 	    memcmp(type, VDEV_TYPE_DISK, len) != 0 &&
190222028508SToomas Soome 	    memcmp(type, VDEV_TYPE_RAIDZ, len) != 0)
190322028508SToomas Soome 		goto done;
190422028508SToomas Soome 
190522028508SToomas Soome 	if (nvlist_find(vdevs, ZPOOL_CONFIG_ASIZE, DATA_TYPE_UINT64,
190622028508SToomas Soome 	    NULL, &asize, NULL) != 0)
190722028508SToomas Soome 		goto done;
190822028508SToomas Soome 
190922028508SToomas Soome 	if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0) {
191022028508SToomas Soome 		nvlist_t **kids;
191122028508SToomas Soome 		int nkids;
191222028508SToomas Soome 
191322028508SToomas Soome 		if (nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN,
191422028508SToomas Soome 		    DATA_TYPE_NVLIST_ARRAY, &nkids, &kids, NULL) != 0) {
191522028508SToomas Soome 			asize = 0;
191622028508SToomas Soome 			goto done;
191722028508SToomas Soome 		}
191822028508SToomas Soome 
191922028508SToomas Soome 		asize /= nkids;
192022028508SToomas Soome 		for (int i = 0; i < nkids; i++)
192122028508SToomas Soome 			nvlist_destroy(kids[i]);
192222028508SToomas Soome 		free(kids);
192322028508SToomas Soome 	}
192422028508SToomas Soome 
192522028508SToomas Soome 	asize += VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
192622028508SToomas Soome done:
1927*2c45cde9SToomas Soome 	nvlist_destroy(vdevs);
192822028508SToomas Soome 	return (asize);
192922028508SToomas Soome }
193022028508SToomas Soome 
193122028508SToomas Soome static nvlist_t *
vdev_label_read_config(vdev_t * vd,uint64_t txg)193222028508SToomas Soome vdev_label_read_config(vdev_t *vd, uint64_t txg)
193322028508SToomas Soome {
193422028508SToomas Soome 	vdev_phys_t *label;
193522028508SToomas Soome 	uint64_t best_txg = 0;
193622028508SToomas Soome 	uint64_t label_txg = 0;
193722028508SToomas Soome 	uint64_t asize;
193822028508SToomas Soome 	nvlist_t *nvl = NULL, *tmp;
193922028508SToomas Soome 	int error;
194022028508SToomas Soome 
194122028508SToomas Soome 	label = malloc(sizeof (vdev_phys_t));
194222028508SToomas Soome 	if (label == NULL)
194322028508SToomas Soome 		return (NULL);
194422028508SToomas Soome 
194522028508SToomas Soome 	for (int l = 0; l < VDEV_LABELS; l++) {
194622028508SToomas Soome 		if (vdev_label_read(vd, l, label,
194722028508SToomas Soome 		    offsetof(vdev_label_t, vl_vdev_phys),
194822028508SToomas Soome 		    sizeof (vdev_phys_t)))
194922028508SToomas Soome 			continue;
195022028508SToomas Soome 
195122028508SToomas Soome 		tmp = nvlist_import(label->vp_nvlist,
195222028508SToomas Soome 		    sizeof (label->vp_nvlist));
195322028508SToomas Soome 		if (tmp == NULL)
195422028508SToomas Soome 			continue;
195522028508SToomas Soome 
195622028508SToomas Soome 		error = nvlist_find(tmp, ZPOOL_CONFIG_POOL_TXG,
195722028508SToomas Soome 		    DATA_TYPE_UINT64, NULL, &label_txg, NULL);
195822028508SToomas Soome 		if (error != 0 || label_txg == 0) {
195922028508SToomas Soome 			nvlist_destroy(nvl);
196022028508SToomas Soome 			nvl = tmp;
196122028508SToomas Soome 			goto done;
196222028508SToomas Soome 		}
196322028508SToomas Soome 
196422028508SToomas Soome 		if (label_txg <= txg && label_txg > best_txg) {
196522028508SToomas Soome 			best_txg = label_txg;
196622028508SToomas Soome 			nvlist_destroy(nvl);
196722028508SToomas Soome 			nvl = tmp;
196822028508SToomas Soome 			tmp = NULL;
196922028508SToomas Soome 
197022028508SToomas Soome 			/*
197122028508SToomas Soome 			 * Use asize from pool config. We need this
197222028508SToomas Soome 			 * because we can get bad value from BIOS.
197322028508SToomas Soome 			 */
197422028508SToomas Soome 			asize = vdev_get_label_asize(nvl);
197522028508SToomas Soome 			if (asize != 0) {
197622028508SToomas Soome 				vd->v_psize = asize;
197722028508SToomas Soome 			}
197822028508SToomas Soome 		}
197922028508SToomas Soome 		nvlist_destroy(tmp);
198022028508SToomas Soome 	}
198122028508SToomas Soome 
198222028508SToomas Soome 	if (best_txg == 0) {
198322028508SToomas Soome 		nvlist_destroy(nvl);
198422028508SToomas Soome 		nvl = NULL;
198522028508SToomas Soome 	}
198622028508SToomas Soome done:
198722028508SToomas Soome 	free(label);
198822028508SToomas Soome 	return (nvl);
198922028508SToomas Soome }
199022028508SToomas Soome 
199122028508SToomas Soome static void
vdev_uberblock_load(vdev_t * vd,uberblock_t * ub)199222028508SToomas Soome vdev_uberblock_load(vdev_t *vd, uberblock_t *ub)
199322028508SToomas Soome {
199422028508SToomas Soome 	uberblock_t *buf;
199522028508SToomas Soome 
199622028508SToomas Soome 	buf = malloc(VDEV_UBERBLOCK_SIZE(vd));
199722028508SToomas Soome 	if (buf == NULL)
199822028508SToomas Soome 		return;
199922028508SToomas Soome 
200022028508SToomas Soome 	for (int l = 0; l < VDEV_LABELS; l++) {
200122028508SToomas Soome 		for (int n = 0; n < VDEV_UBERBLOCK_COUNT(vd); n++) {
200222028508SToomas Soome 			if (vdev_label_read(vd, l, buf,
200322028508SToomas Soome 			    VDEV_UBERBLOCK_OFFSET(vd, n),
200422028508SToomas Soome 			    VDEV_UBERBLOCK_SIZE(vd)))
200522028508SToomas Soome 				continue;
200622028508SToomas Soome 			if (uberblock_verify(buf) != 0)
200722028508SToomas Soome 				continue;
200822028508SToomas Soome 
200922028508SToomas Soome 			if (vdev_uberblock_compare(buf, ub) > 0)
201022028508SToomas Soome 				*ub = *buf;
201122028508SToomas Soome 		}
201222028508SToomas Soome 	}
201322028508SToomas Soome 	free(buf);
201422028508SToomas Soome }
201522028508SToomas Soome 
201622028508SToomas Soome static int
vdev_probe(vdev_phys_read_t * _read,vdev_phys_write_t * _write,void * priv,spa_t ** spap)201722028508SToomas Soome vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv,
201822028508SToomas Soome     spa_t **spap)
201922028508SToomas Soome {
202022028508SToomas Soome 	vdev_t vtmp;
202122028508SToomas Soome 	spa_t *spa;
202222028508SToomas Soome 	vdev_t *vdev;
202322028508SToomas Soome 	nvlist_t *nvl;
202422028508SToomas Soome 	uint64_t val;
202522028508SToomas Soome 	uint64_t guid, vdev_children;
202622028508SToomas Soome 	uint64_t pool_txg, pool_guid;
202722028508SToomas Soome 	const char *pool_name;
202822028508SToomas Soome 	int rc, namelen;
202922028508SToomas Soome 
203022028508SToomas Soome 	/*
203122028508SToomas Soome 	 * Load the vdev label and figure out which
203222028508SToomas Soome 	 * uberblock is most current.
203322028508SToomas Soome 	 */
203422028508SToomas Soome 	memset(&vtmp, 0, sizeof (vtmp));
203522028508SToomas Soome 	vtmp.v_phys_read = _read;
203622028508SToomas Soome 	vtmp.v_phys_write = _write;
203722028508SToomas Soome 	vtmp.v_priv = priv;
203822028508SToomas Soome 	vtmp.v_psize = P2ALIGN(ldi_get_size(priv),
203922028508SToomas Soome 	    (uint64_t)sizeof (vdev_label_t));
204022028508SToomas Soome 
204122028508SToomas Soome 	/* Test for minimum device size. */
204222028508SToomas Soome 	if (vtmp.v_psize < SPA_MINDEVSIZE)
204322028508SToomas Soome 		return (EIO);
204422028508SToomas Soome 
204522028508SToomas Soome 	nvl = vdev_label_read_config(&vtmp, UINT64_MAX);
204622028508SToomas Soome 	if (nvl == NULL)
204722028508SToomas Soome 		return (EIO);
204822028508SToomas Soome 
204922028508SToomas Soome 	if (nvlist_find(nvl, ZPOOL_CONFIG_VERSION, DATA_TYPE_UINT64,
205022028508SToomas Soome 	    NULL, &val, NULL) != 0) {
205122028508SToomas Soome 		nvlist_destroy(nvl);
205222028508SToomas Soome 		return (EIO);
205322028508SToomas Soome 	}
205422028508SToomas Soome 
205522028508SToomas Soome 	if (!SPA_VERSION_IS_SUPPORTED(val)) {
205622028508SToomas Soome 		printf("ZFS: unsupported ZFS version %u (should be %u)\n",
205722028508SToomas Soome 		    (unsigned)val, (unsigned)SPA_VERSION);
205822028508SToomas Soome 		nvlist_destroy(nvl);
205922028508SToomas Soome 		return (EIO);
206022028508SToomas Soome 	}
206122028508SToomas Soome 
206222028508SToomas Soome 	/* Check ZFS features for read */
206322028508SToomas Soome 	rc = nvlist_check_features_for_read(nvl);
206422028508SToomas Soome 	if (rc != 0) {
206522028508SToomas Soome 		nvlist_destroy(nvl);
206622028508SToomas Soome 		return (EIO);
206722028508SToomas Soome 	}
206822028508SToomas Soome 
206922028508SToomas Soome 	if (nvlist_find(nvl, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64,
207022028508SToomas Soome 	    NULL, &val, NULL) != 0) {
207122028508SToomas Soome 		nvlist_destroy(nvl);
207222028508SToomas Soome 		return (EIO);
207322028508SToomas Soome 	}
207422028508SToomas Soome 
207522028508SToomas Soome 	if (val == POOL_STATE_DESTROYED) {
207622028508SToomas Soome 		/* We don't boot only from destroyed pools. */
207722028508SToomas Soome 		nvlist_destroy(nvl);
207822028508SToomas Soome 		return (EIO);
207922028508SToomas Soome 	}
208022028508SToomas Soome 
208122028508SToomas Soome 	if (nvlist_find(nvl, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64,
208222028508SToomas Soome 	    NULL, &pool_txg, NULL) != 0 ||
208322028508SToomas Soome 	    nvlist_find(nvl, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
208422028508SToomas Soome 	    NULL, &pool_guid, NULL) != 0 ||
208522028508SToomas Soome 	    nvlist_find(nvl, ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING,
208622028508SToomas Soome 	    NULL, &pool_name, &namelen) != 0) {
208722028508SToomas Soome 		/*
208822028508SToomas Soome 		 * Cache and spare devices end up here - just ignore
208922028508SToomas Soome 		 * them.
209022028508SToomas Soome 		 */
209122028508SToomas Soome 		nvlist_destroy(nvl);
209222028508SToomas Soome 		return (EIO);
209322028508SToomas Soome 	}
209422028508SToomas Soome 
209522028508SToomas Soome 	/*
209622028508SToomas Soome 	 * Create the pool if this is the first time we've seen it.
209722028508SToomas Soome 	 */
209822028508SToomas Soome 	spa = spa_find_by_guid(pool_guid);
209922028508SToomas Soome 	if (spa == NULL) {
210022028508SToomas Soome 		char *name;
210122028508SToomas Soome 
210222028508SToomas Soome 		nvlist_find(nvl, ZPOOL_CONFIG_VDEV_CHILDREN,
210322028508SToomas Soome 		    DATA_TYPE_UINT64, NULL, &vdev_children, NULL);
210422028508SToomas Soome 		name = malloc(namelen + 1);
210522028508SToomas Soome 		if (name == NULL) {
210622028508SToomas Soome 			nvlist_destroy(nvl);
210722028508SToomas Soome 			return (ENOMEM);
210822028508SToomas Soome 		}
210922028508SToomas Soome 		bcopy(pool_name, name, namelen);
211022028508SToomas Soome 		name[namelen] = '\0';
211122028508SToomas Soome 		spa = spa_create(pool_guid, name);
211222028508SToomas Soome 		free(name);
211322028508SToomas Soome 		if (spa == NULL) {
211422028508SToomas Soome 			nvlist_destroy(nvl);
211522028508SToomas Soome 			return (ENOMEM);
211622028508SToomas Soome 		}
211722028508SToomas Soome 		spa->spa_root_vdev->v_nchildren = vdev_children;
211822028508SToomas Soome 	}
211922028508SToomas Soome 	if (pool_txg > spa->spa_txg)
212022028508SToomas Soome 		spa->spa_txg = pool_txg;
212122028508SToomas Soome 
212222028508SToomas Soome 	/*
212322028508SToomas Soome 	 * Get the vdev tree and create our in-core copy of it.
212422028508SToomas Soome 	 * If we already have a vdev with this guid, this must
212522028508SToomas Soome 	 * be some kind of alias (overlapping slices, dangerously dedicated
212622028508SToomas Soome 	 * disks etc).
212722028508SToomas Soome 	 */
212822028508SToomas Soome 	if (nvlist_find(nvl, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
212922028508SToomas Soome 	    NULL, &guid, NULL) != 0) {
213022028508SToomas Soome 		nvlist_destroy(nvl);
213122028508SToomas Soome 		return (EIO);
213222028508SToomas Soome 	}
213322028508SToomas Soome 	vdev = vdev_find(guid);
213422028508SToomas Soome 	/* Has this vdev already been inited? */
213522028508SToomas Soome 	if (vdev && vdev->v_phys_read) {
213622028508SToomas Soome 		nvlist_destroy(nvl);
213722028508SToomas Soome 		return (EIO);
213822028508SToomas Soome 	}
213922028508SToomas Soome 
214022028508SToomas Soome 	rc = vdev_init_from_label(spa, nvl);
214122028508SToomas Soome 	nvlist_destroy(nvl);
214222028508SToomas Soome 	if (rc != 0)
214322028508SToomas Soome 		return (rc);
214422028508SToomas Soome 
214522028508SToomas Soome 	/*
214622028508SToomas Soome 	 * We should already have created an incomplete vdev for this
214722028508SToomas Soome 	 * vdev. Find it and initialise it with our read proc.
214822028508SToomas Soome 	 */
214922028508SToomas Soome 	vdev = vdev_find(guid);
215022028508SToomas Soome 	if (vdev != NULL) {
215122028508SToomas Soome 		vdev->v_phys_read = _read;
215222028508SToomas Soome 		vdev->v_phys_write = _write;
215322028508SToomas Soome 		vdev->v_priv = priv;
215422028508SToomas Soome 		vdev->v_psize = vtmp.v_psize;
215522028508SToomas Soome 		/*
215622028508SToomas Soome 		 * If no other state is set, mark vdev healthy.
215722028508SToomas Soome 		 */
215822028508SToomas Soome 		if (vdev->v_state == VDEV_STATE_UNKNOWN)
215922028508SToomas Soome 			vdev->v_state = VDEV_STATE_HEALTHY;
216022028508SToomas Soome 	} else {
216122028508SToomas Soome 		printf("ZFS: inconsistent nvlist contents\n");
216222028508SToomas Soome 		return (EIO);
216322028508SToomas Soome 	}
216422028508SToomas Soome 
216522028508SToomas Soome 	if (vdev->v_islog)
216622028508SToomas Soome 		spa->spa_with_log = vdev->v_islog;
216722028508SToomas Soome 
216822028508SToomas Soome 	/* Record boot vdev for spa. */
216922028508SToomas Soome 	if (spa->spa_boot_vdev == NULL)
217022028508SToomas Soome 		spa->spa_boot_vdev = vdev;
217122028508SToomas Soome 
217222028508SToomas Soome 	/*
217322028508SToomas Soome 	 * Re-evaluate top-level vdev state.
217422028508SToomas Soome 	 */
217522028508SToomas Soome 	vdev_set_state(vdev->v_top);
217622028508SToomas Soome 
217722028508SToomas Soome 	/*
217822028508SToomas Soome 	 * Ok, we are happy with the pool so far. Lets find
217922028508SToomas Soome 	 * the best uberblock and then we can actually access
218022028508SToomas Soome 	 * the contents of the pool.
218122028508SToomas Soome 	 */
218222028508SToomas Soome 	vdev_uberblock_load(vdev, &spa->spa_uberblock);
218322028508SToomas Soome 
218422028508SToomas Soome 	if (spap != NULL)
218522028508SToomas Soome 		*spap = spa;
218622028508SToomas Soome 	return (0);
218722028508SToomas Soome }
218822028508SToomas Soome 
218922028508SToomas Soome static int
ilog2(int n)219022028508SToomas Soome ilog2(int n)
219122028508SToomas Soome {
219222028508SToomas Soome 	int v;
219322028508SToomas Soome 
219422028508SToomas Soome 	for (v = 0; v < 32; v++)
219522028508SToomas Soome 		if (n == (1 << v))
219622028508SToomas Soome 			return (v);
219722028508SToomas Soome 	return (-1);
219822028508SToomas Soome }
219922028508SToomas Soome 
220022028508SToomas Soome static int
zio_read_gang(const spa_t * spa,const blkptr_t * bp,void * buf)220122028508SToomas Soome zio_read_gang(const spa_t *spa, const blkptr_t *bp, void *buf)
220222028508SToomas Soome {
220322028508SToomas Soome 	blkptr_t gbh_bp;
220422028508SToomas Soome 	zio_gbh_phys_t zio_gb;
220522028508SToomas Soome 	char *pbuf;
220622028508SToomas Soome 	int i;
220722028508SToomas Soome 
220822028508SToomas Soome 	/* Artificial BP for gang block header. */
220922028508SToomas Soome 	gbh_bp = *bp;
221022028508SToomas Soome 	BP_SET_PSIZE(&gbh_bp, SPA_GANGBLOCKSIZE);
221122028508SToomas Soome 	BP_SET_LSIZE(&gbh_bp, SPA_GANGBLOCKSIZE);
221222028508SToomas Soome 	BP_SET_CHECKSUM(&gbh_bp, ZIO_CHECKSUM_GANG_HEADER);
221322028508SToomas Soome 	BP_SET_COMPRESS(&gbh_bp, ZIO_COMPRESS_OFF);
221422028508SToomas Soome 	for (i = 0; i < SPA_DVAS_PER_BP; i++)
221522028508SToomas Soome 		DVA_SET_GANG(&gbh_bp.blk_dva[i], 0);
221622028508SToomas Soome 
221722028508SToomas Soome 	/* Read gang header block using the artificial BP. */
221822028508SToomas Soome 	if (zio_read(spa, &gbh_bp, &zio_gb))
221922028508SToomas Soome 		return (EIO);
222022028508SToomas Soome 
222122028508SToomas Soome 	pbuf = buf;
222222028508SToomas Soome 	for (i = 0; i < SPA_GBH_NBLKPTRS; i++) {
222322028508SToomas Soome 		blkptr_t *gbp = &zio_gb.zg_blkptr[i];
222422028508SToomas Soome 
222522028508SToomas Soome 		if (BP_IS_HOLE(gbp))
222622028508SToomas Soome 			continue;
222722028508SToomas Soome 		if (zio_read(spa, gbp, pbuf))
222822028508SToomas Soome 			return (EIO);
222922028508SToomas Soome 		pbuf += BP_GET_PSIZE(gbp);
223022028508SToomas Soome 	}
223122028508SToomas Soome 
223222028508SToomas Soome 	if (zio_checksum_verify(spa, bp, buf))
223322028508SToomas Soome 		return (EIO);
223422028508SToomas Soome 	return (0);
223522028508SToomas Soome }
223622028508SToomas Soome 
223722028508SToomas Soome static int
zio_read(const spa_t * spa,const blkptr_t * bp,void * buf)223822028508SToomas Soome zio_read(const spa_t *spa, const blkptr_t *bp, void *buf)
223922028508SToomas Soome {
224022028508SToomas Soome 	int cpfunc = BP_GET_COMPRESS(bp);
224122028508SToomas Soome 	uint64_t align, size;
224222028508SToomas Soome 	void *pbuf;
224322028508SToomas Soome 	int i, error;
224422028508SToomas Soome 
224522028508SToomas Soome 	/*
224622028508SToomas Soome 	 * Process data embedded in block pointer
224722028508SToomas Soome 	 */
224822028508SToomas Soome 	if (BP_IS_EMBEDDED(bp)) {
224922028508SToomas Soome 		ASSERT(BPE_GET_ETYPE(bp) == BP_EMBEDDED_TYPE_DATA);
225022028508SToomas Soome 
225122028508SToomas Soome 		size = BPE_GET_PSIZE(bp);
225222028508SToomas Soome 		ASSERT(size <= BPE_PAYLOAD_SIZE);
225322028508SToomas Soome 
225422028508SToomas Soome 		if (cpfunc != ZIO_COMPRESS_OFF)
225522028508SToomas Soome 			pbuf = malloc(size);
225622028508SToomas Soome 		else
225722028508SToomas Soome 			pbuf = buf;
225822028508SToomas Soome 
225922028508SToomas Soome 		if (pbuf == NULL)
226022028508SToomas Soome 			return (ENOMEM);
226122028508SToomas Soome 
226222028508SToomas Soome 		decode_embedded_bp_compressed(bp, pbuf);
226322028508SToomas Soome 		error = 0;
226422028508SToomas Soome 
226522028508SToomas Soome 		if (cpfunc != ZIO_COMPRESS_OFF) {
226622028508SToomas Soome 			error = zio_decompress_data(cpfunc, pbuf,
226722028508SToomas Soome 			    size, buf, BP_GET_LSIZE(bp));
226822028508SToomas Soome 			free(pbuf);
226922028508SToomas Soome 		}
227022028508SToomas Soome 		if (error != 0)
227122028508SToomas Soome 			printf("ZFS: i/o error - unable to decompress "
227222028508SToomas Soome 			    "block pointer data, error %d\n", error);
227322028508SToomas Soome 		return (error);
227422028508SToomas Soome 	}
227522028508SToomas Soome 
227622028508SToomas Soome 	error = EIO;
227722028508SToomas Soome 
227822028508SToomas Soome 	for (i = 0; i < SPA_DVAS_PER_BP; i++) {
227922028508SToomas Soome 		const dva_t *dva = &bp->blk_dva[i];
228022028508SToomas Soome 		vdev_t *vdev;
228122028508SToomas Soome 		vdev_list_t *vlist;
228222028508SToomas Soome 		uint64_t vdevid;
228322028508SToomas Soome 		off_t offset;
228422028508SToomas Soome 
228522028508SToomas Soome 		if (!dva->dva_word[0] && !dva->dva_word[1])
228622028508SToomas Soome 			continue;
228722028508SToomas Soome 
228822028508SToomas Soome 		vdevid = DVA_GET_VDEV(dva);
228922028508SToomas Soome 		offset = DVA_GET_OFFSET(dva);
229022028508SToomas Soome 		vlist = &spa->spa_root_vdev->v_children;
229122028508SToomas Soome 		STAILQ_FOREACH(vdev, vlist, v_childlink) {
229222028508SToomas Soome 			if (vdev->v_id == vdevid)
229322028508SToomas Soome 				break;
229422028508SToomas Soome 		}
229522028508SToomas Soome 		if (!vdev || !vdev->v_read)
229622028508SToomas Soome 			continue;
229722028508SToomas Soome 
229822028508SToomas Soome 		size = BP_GET_PSIZE(bp);
229922028508SToomas Soome 		if (vdev->v_read == vdev_raidz_read) {
230022028508SToomas Soome 			align = 1ULL << vdev->v_ashift;
230122028508SToomas Soome 			if (P2PHASE(size, align) != 0)
230222028508SToomas Soome 				size = P2ROUNDUP(size, align);
230322028508SToomas Soome 		}
230422028508SToomas Soome 		if (size != BP_GET_PSIZE(bp) || cpfunc != ZIO_COMPRESS_OFF)
230522028508SToomas Soome 			pbuf = malloc(size);
230622028508SToomas Soome 		else
230722028508SToomas Soome 			pbuf = buf;
230822028508SToomas Soome 
230922028508SToomas Soome 		if (pbuf == NULL) {
231022028508SToomas Soome 			error = ENOMEM;
231122028508SToomas Soome 			break;
231222028508SToomas Soome 		}
231322028508SToomas Soome 
231422028508SToomas Soome 		if (DVA_GET_GANG(dva))
231522028508SToomas Soome 			error = zio_read_gang(spa, bp, pbuf);
231622028508SToomas Soome 		else
231722028508SToomas Soome 			error = vdev->v_read(vdev, bp, pbuf, offset, size);
231822028508SToomas Soome 		if (error == 0) {
231922028508SToomas Soome 			if (cpfunc != ZIO_COMPRESS_OFF)
232022028508SToomas Soome 				error = zio_decompress_data(cpfunc, pbuf,
232122028508SToomas Soome 				    BP_GET_PSIZE(bp), buf, BP_GET_LSIZE(bp));
232222028508SToomas Soome 			else if (size != BP_GET_PSIZE(bp))
232322028508SToomas Soome 				bcopy(pbuf, buf, BP_GET_PSIZE(bp));
232422028508SToomas Soome 		}
232522028508SToomas Soome 		if (buf != pbuf)
232622028508SToomas Soome 			free(pbuf);
232722028508SToomas Soome 		if (error == 0)
232822028508SToomas Soome 			break;
232922028508SToomas Soome 	}
233022028508SToomas Soome 	if (error != 0)
233122028508SToomas Soome 		printf("ZFS: i/o error - all block copies unavailable\n");
233222028508SToomas Soome 
233322028508SToomas Soome 	return (error);
233422028508SToomas Soome }
233522028508SToomas Soome 
233622028508SToomas Soome static int
dnode_read(const spa_t * spa,const dnode_phys_t * dnode,off_t offset,void * buf,size_t buflen)233722028508SToomas Soome dnode_read(const spa_t *spa, const dnode_phys_t *dnode, off_t offset,
233822028508SToomas Soome     void *buf, size_t buflen)
233922028508SToomas Soome {
234022028508SToomas Soome 	int ibshift = dnode->dn_indblkshift - SPA_BLKPTRSHIFT;
234122028508SToomas Soome 	int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
234222028508SToomas Soome 	int nlevels = dnode->dn_nlevels;
234322028508SToomas Soome 	int i, rc;
234422028508SToomas Soome 
234522028508SToomas Soome 	if (bsize > SPA_MAXBLOCKSIZE) {
234622028508SToomas Soome 		printf("ZFS: I/O error - blocks larger than %llu are not "
234722028508SToomas Soome 		    "supported\n", SPA_MAXBLOCKSIZE);
234822028508SToomas Soome 		return (EIO);
234922028508SToomas Soome 	}
235022028508SToomas Soome 
235122028508SToomas Soome 	/*
235277570342SKyle Evans 	 * Handle odd block sizes, mirrors dmu_read_impl().  Data can't exist
235377570342SKyle Evans 	 * past the first block, so we'll clip the read to the portion of the
235477570342SKyle Evans 	 * buffer within bsize and zero out the remainder.
235577570342SKyle Evans 	 */
235677570342SKyle Evans 	if (dnode->dn_maxblkid == 0) {
235777570342SKyle Evans 		size_t newbuflen;
235877570342SKyle Evans 
235977570342SKyle Evans 		newbuflen = offset > bsize ? 0 : MIN(buflen, bsize - offset);
236077570342SKyle Evans 		bzero((char *)buf + newbuflen, buflen - newbuflen);
236177570342SKyle Evans 		buflen = newbuflen;
236277570342SKyle Evans 	}
236377570342SKyle Evans 
236477570342SKyle Evans 	/*
236522028508SToomas Soome 	 * Note: bsize may not be a power of two here so we need to do an
236622028508SToomas Soome 	 * actual divide rather than a bitshift.
236722028508SToomas Soome 	 */
236822028508SToomas Soome 	while (buflen > 0) {
236922028508SToomas Soome 		uint64_t bn = offset / bsize;
237022028508SToomas Soome 		int boff = offset % bsize;
237122028508SToomas Soome 		int ibn;
237222028508SToomas Soome 		const blkptr_t *indbp;
237322028508SToomas Soome 		blkptr_t bp;
237422028508SToomas Soome 
237522028508SToomas Soome 		if (bn > dnode->dn_maxblkid) {
237622028508SToomas Soome 			printf("warning: zfs bug: bn %llx > dn_maxblkid %llx\n",
237722028508SToomas Soome 			    (unsigned long long)bn,
237822028508SToomas Soome 			    (unsigned long long)dnode->dn_maxblkid);
237922028508SToomas Soome 			/*
238022028508SToomas Soome 			 * zfs bug, will not return error
238122028508SToomas Soome 			 * return (EIO);
238222028508SToomas Soome 			 */
238322028508SToomas Soome 		}
238422028508SToomas Soome 
238522028508SToomas Soome 		if (dnode == dnode_cache_obj && bn == dnode_cache_bn)
238622028508SToomas Soome 			goto cached;
238722028508SToomas Soome 
238822028508SToomas Soome 		indbp = dnode->dn_blkptr;
238922028508SToomas Soome 		for (i = 0; i < nlevels; i++) {
239022028508SToomas Soome 			/*
239122028508SToomas Soome 			 * Copy the bp from the indirect array so that
239222028508SToomas Soome 			 * we can re-use the scratch buffer for multi-level
239322028508SToomas Soome 			 * objects.
239422028508SToomas Soome 			 */
239522028508SToomas Soome 			ibn = bn >> ((nlevels - i - 1) * ibshift);
239622028508SToomas Soome 			ibn &= ((1 << ibshift) - 1);
239722028508SToomas Soome 			bp = indbp[ibn];
239822028508SToomas Soome 			if (BP_IS_HOLE(&bp)) {
239922028508SToomas Soome 				memset(dnode_cache_buf, 0, bsize);
240022028508SToomas Soome 				break;
240122028508SToomas Soome 			}
240222028508SToomas Soome 			rc = zio_read(spa, &bp, dnode_cache_buf);
240322028508SToomas Soome 			if (rc)
240422028508SToomas Soome 				return (rc);
240522028508SToomas Soome 			indbp = (const blkptr_t *) dnode_cache_buf;
240622028508SToomas Soome 		}
240722028508SToomas Soome 		dnode_cache_obj = dnode;
240822028508SToomas Soome 		dnode_cache_bn = bn;
240922028508SToomas Soome 	cached:
241022028508SToomas Soome 
241122028508SToomas Soome 		/*
241222028508SToomas Soome 		 * The buffer contains our data block. Copy what we
241322028508SToomas Soome 		 * need from it and loop.
241422028508SToomas Soome 		 */
241522028508SToomas Soome 		i = bsize - boff;
241622028508SToomas Soome 		if (i > buflen) i = buflen;
241722028508SToomas Soome 		memcpy(buf, &dnode_cache_buf[boff], i);
241822028508SToomas Soome 		buf = ((char *)buf) + i;
241922028508SToomas Soome 		offset += i;
242022028508SToomas Soome 		buflen -= i;
242122028508SToomas Soome 	}
242222028508SToomas Soome 
242322028508SToomas Soome 	return (0);
242422028508SToomas Soome }
242522028508SToomas Soome 
242622028508SToomas Soome /*
242722028508SToomas Soome  * Lookup a value in a microzap directory.
242822028508SToomas Soome  */
242922028508SToomas Soome static int
mzap_lookup(const mzap_phys_t * mz,size_t size,const char * name,uint64_t * value)243022028508SToomas Soome mzap_lookup(const mzap_phys_t *mz, size_t size, const char *name,
243122028508SToomas Soome     uint64_t *value)
243222028508SToomas Soome {
243322028508SToomas Soome 	const mzap_ent_phys_t *mze;
243422028508SToomas Soome 	int chunks, i;
243522028508SToomas Soome 
243622028508SToomas Soome 	/*
243722028508SToomas Soome 	 * Microzap objects use exactly one block. Read the whole
243822028508SToomas Soome 	 * thing.
243922028508SToomas Soome 	 */
244022028508SToomas Soome 	chunks = size / MZAP_ENT_LEN - 1;
244122028508SToomas Soome 	for (i = 0; i < chunks; i++) {
244222028508SToomas Soome 		mze = &mz->mz_chunk[i];
244322028508SToomas Soome 		if (strcmp(mze->mze_name, name) == 0) {
244422028508SToomas Soome 			*value = mze->mze_value;
244522028508SToomas Soome 			return (0);
244622028508SToomas Soome 		}
244722028508SToomas Soome 	}
244822028508SToomas Soome 
244922028508SToomas Soome 	return (ENOENT);
245022028508SToomas Soome }
245122028508SToomas Soome 
245222028508SToomas Soome /*
245322028508SToomas Soome  * Compare a name with a zap leaf entry. Return non-zero if the name
245422028508SToomas Soome  * matches.
245522028508SToomas Soome  */
245622028508SToomas Soome static int
fzap_name_equal(const zap_leaf_t * zl,const zap_leaf_chunk_t * zc,const char * name)245722028508SToomas Soome fzap_name_equal(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc,
245822028508SToomas Soome     const char *name)
245922028508SToomas Soome {
246022028508SToomas Soome 	size_t namelen;
246122028508SToomas Soome 	const zap_leaf_chunk_t *nc;
246222028508SToomas Soome 	const char *p;
246322028508SToomas Soome 
246422028508SToomas Soome 	namelen = zc->l_entry.le_name_numints;
246522028508SToomas Soome 
246622028508SToomas Soome 	nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk);
246722028508SToomas Soome 	p = name;
246822028508SToomas Soome 	while (namelen > 0) {
246922028508SToomas Soome 		size_t len;
247022028508SToomas Soome 
247122028508SToomas Soome 		len = namelen;
247222028508SToomas Soome 		if (len > ZAP_LEAF_ARRAY_BYTES)
247322028508SToomas Soome 			len = ZAP_LEAF_ARRAY_BYTES;
247422028508SToomas Soome 		if (memcmp(p, nc->l_array.la_array, len))
247522028508SToomas Soome 			return (0);
247622028508SToomas Soome 		p += len;
247722028508SToomas Soome 		namelen -= len;
247822028508SToomas Soome 		nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next);
247922028508SToomas Soome 	}
248022028508SToomas Soome 
248122028508SToomas Soome 	return (1);
248222028508SToomas Soome }
248322028508SToomas Soome 
248422028508SToomas Soome /*
248522028508SToomas Soome  * Extract a uint64_t value from a zap leaf entry.
248622028508SToomas Soome  */
248722028508SToomas Soome static uint64_t
fzap_leaf_value(const zap_leaf_t * zl,const zap_leaf_chunk_t * zc)248822028508SToomas Soome fzap_leaf_value(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc)
248922028508SToomas Soome {
249022028508SToomas Soome 	const zap_leaf_chunk_t *vc;
249122028508SToomas Soome 	int i;
249222028508SToomas Soome 	uint64_t value;
249322028508SToomas Soome 	const uint8_t *p;
249422028508SToomas Soome 
249522028508SToomas Soome 	vc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_value_chunk);
249622028508SToomas Soome 	for (i = 0, value = 0, p = vc->l_array.la_array; i < 8; i++) {
249722028508SToomas Soome 		value = (value << 8) | p[i];
249822028508SToomas Soome 	}
249922028508SToomas Soome 
250022028508SToomas Soome 	return (value);
250122028508SToomas Soome }
250222028508SToomas Soome 
250322028508SToomas Soome static void
stv(int len,void * addr,uint64_t value)250422028508SToomas Soome stv(int len, void *addr, uint64_t value)
250522028508SToomas Soome {
250622028508SToomas Soome 	switch (len) {
250722028508SToomas Soome 	case 1:
250822028508SToomas Soome 		*(uint8_t *)addr = value;
250922028508SToomas Soome 		return;
251022028508SToomas Soome 	case 2:
251122028508SToomas Soome 		*(uint16_t *)addr = value;
251222028508SToomas Soome 		return;
251322028508SToomas Soome 	case 4:
251422028508SToomas Soome 		*(uint32_t *)addr = value;
251522028508SToomas Soome 		return;
251622028508SToomas Soome 	case 8:
251722028508SToomas Soome 		*(uint64_t *)addr = value;
251822028508SToomas Soome 		return;
251922028508SToomas Soome 	}
252022028508SToomas Soome }
252122028508SToomas Soome 
252222028508SToomas Soome /*
252322028508SToomas Soome  * Extract a array from a zap leaf entry.
252422028508SToomas Soome  */
252522028508SToomas Soome static void
fzap_leaf_array(const zap_leaf_t * zl,const zap_leaf_chunk_t * zc,uint64_t integer_size,uint64_t num_integers,void * buf)252622028508SToomas Soome fzap_leaf_array(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc,
252722028508SToomas Soome     uint64_t integer_size, uint64_t num_integers, void *buf)
252822028508SToomas Soome {
252922028508SToomas Soome 	uint64_t array_int_len = zc->l_entry.le_value_intlen;
253022028508SToomas Soome 	uint64_t value = 0;
253122028508SToomas Soome 	uint64_t *u64 = buf;
253222028508SToomas Soome 	char *p = buf;
253322028508SToomas Soome 	int len = MIN(zc->l_entry.le_value_numints, num_integers);
253422028508SToomas Soome 	int chunk = zc->l_entry.le_value_chunk;
253522028508SToomas Soome 	int byten = 0;
253622028508SToomas Soome 
253722028508SToomas Soome 	if (integer_size == 8 && len == 1) {
253822028508SToomas Soome 		*u64 = fzap_leaf_value(zl, zc);
253922028508SToomas Soome 		return;
254022028508SToomas Soome 	}
254122028508SToomas Soome 
254222028508SToomas Soome 	while (len > 0) {
254322028508SToomas Soome 		struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(zl, chunk).l_array;
254422028508SToomas Soome 		int i;
254522028508SToomas Soome 
254622028508SToomas Soome 		ASSERT3U(chunk, <, ZAP_LEAF_NUMCHUNKS(zl));
254722028508SToomas Soome 		for (i = 0; i < ZAP_LEAF_ARRAY_BYTES && len > 0; i++) {
254822028508SToomas Soome 			value = (value << 8) | la->la_array[i];
254922028508SToomas Soome 			byten++;
255022028508SToomas Soome 			if (byten == array_int_len) {
255122028508SToomas Soome 				stv(integer_size, p, value);
255222028508SToomas Soome 				byten = 0;
255322028508SToomas Soome 				len--;
255422028508SToomas Soome 				if (len == 0)
255522028508SToomas Soome 					return;
255622028508SToomas Soome 				p += integer_size;
255722028508SToomas Soome 			}
255822028508SToomas Soome 		}
255922028508SToomas Soome 		chunk = la->la_next;
256022028508SToomas Soome 	}
256122028508SToomas Soome }
256222028508SToomas Soome 
256322028508SToomas Soome static int
fzap_check_size(uint64_t integer_size,uint64_t num_integers)256422028508SToomas Soome fzap_check_size(uint64_t integer_size, uint64_t num_integers)
256522028508SToomas Soome {
256622028508SToomas Soome 
256722028508SToomas Soome 	switch (integer_size) {
256822028508SToomas Soome 	case 1:
256922028508SToomas Soome 	case 2:
257022028508SToomas Soome 	case 4:
257122028508SToomas Soome 	case 8:
257222028508SToomas Soome 		break;
257322028508SToomas Soome 	default:
257422028508SToomas Soome 		return (EINVAL);
257522028508SToomas Soome 	}
257622028508SToomas Soome 
257722028508SToomas Soome 	if (integer_size * num_integers > ZAP_MAXVALUELEN)
257822028508SToomas Soome 		return (E2BIG);
257922028508SToomas Soome 
258022028508SToomas Soome 	return (0);
258122028508SToomas Soome }
258222028508SToomas Soome 
258322028508SToomas Soome static void
zap_leaf_free(zap_leaf_t * leaf)258422028508SToomas Soome zap_leaf_free(zap_leaf_t *leaf)
258522028508SToomas Soome {
258622028508SToomas Soome 	free(leaf->l_phys);
258722028508SToomas Soome 	free(leaf);
258822028508SToomas Soome }
258922028508SToomas Soome 
259022028508SToomas Soome static int
zap_get_leaf_byblk(fat_zap_t * zap,uint64_t blk,zap_leaf_t ** lp)259122028508SToomas Soome zap_get_leaf_byblk(fat_zap_t *zap, uint64_t blk, zap_leaf_t **lp)
259222028508SToomas Soome {
259322028508SToomas Soome 	int bs = FZAP_BLOCK_SHIFT(zap);
259422028508SToomas Soome 	int err;
259522028508SToomas Soome 
259622028508SToomas Soome 	*lp = malloc(sizeof (**lp));
259722028508SToomas Soome 	if (*lp == NULL)
259822028508SToomas Soome 		return (ENOMEM);
259922028508SToomas Soome 
260022028508SToomas Soome 	(*lp)->l_bs = bs;
260122028508SToomas Soome 	(*lp)->l_phys = malloc(1 << bs);
260222028508SToomas Soome 
260322028508SToomas Soome 	if ((*lp)->l_phys == NULL) {
260422028508SToomas Soome 		free(*lp);
260522028508SToomas Soome 		return (ENOMEM);
260622028508SToomas Soome 	}
260722028508SToomas Soome 	err = dnode_read(zap->zap_spa, zap->zap_dnode, blk << bs, (*lp)->l_phys,
260822028508SToomas Soome 	    1 << bs);
260922028508SToomas Soome 	if (err != 0) {
261022028508SToomas Soome 		zap_leaf_free(*lp);
261122028508SToomas Soome 	}
261222028508SToomas Soome 	return (err);
261322028508SToomas Soome }
261422028508SToomas Soome 
261522028508SToomas Soome static int
zap_table_load(fat_zap_t * zap,zap_table_phys_t * tbl,uint64_t idx,uint64_t * valp)261622028508SToomas Soome zap_table_load(fat_zap_t *zap, zap_table_phys_t *tbl, uint64_t idx,
261722028508SToomas Soome     uint64_t *valp)
261822028508SToomas Soome {
261922028508SToomas Soome 	int bs = FZAP_BLOCK_SHIFT(zap);
262022028508SToomas Soome 	uint64_t blk = idx >> (bs - 3);
262122028508SToomas Soome 	uint64_t off = idx & ((1 << (bs - 3)) - 1);
262222028508SToomas Soome 	uint64_t *buf;
262322028508SToomas Soome 	int rc;
262422028508SToomas Soome 
262522028508SToomas Soome 	buf = malloc(1 << zap->zap_block_shift);
262622028508SToomas Soome 	if (buf == NULL)
262722028508SToomas Soome 		return (ENOMEM);
262822028508SToomas Soome 	rc = dnode_read(zap->zap_spa, zap->zap_dnode, (tbl->zt_blk + blk) << bs,
262922028508SToomas Soome 	    buf, 1 << zap->zap_block_shift);
263022028508SToomas Soome 	if (rc == 0)
263122028508SToomas Soome 		*valp = buf[off];
263222028508SToomas Soome 	free(buf);
263322028508SToomas Soome 	return (rc);
263422028508SToomas Soome }
263522028508SToomas Soome 
263622028508SToomas Soome static int
zap_idx_to_blk(fat_zap_t * zap,uint64_t idx,uint64_t * valp)263722028508SToomas Soome zap_idx_to_blk(fat_zap_t *zap, uint64_t idx, uint64_t *valp)
263822028508SToomas Soome {
263922028508SToomas Soome 	if (zap->zap_phys->zap_ptrtbl.zt_numblks == 0) {
264022028508SToomas Soome 		*valp = ZAP_EMBEDDED_PTRTBL_ENT(zap, idx);
264122028508SToomas Soome 		return (0);
264222028508SToomas Soome 	} else {
264322028508SToomas Soome 		return (zap_table_load(zap, &zap->zap_phys->zap_ptrtbl,
264422028508SToomas Soome 		    idx, valp));
264522028508SToomas Soome 	}
264622028508SToomas Soome }
264722028508SToomas Soome 
264822028508SToomas Soome #define	ZAP_HASH_IDX(hash, n)	(((n) == 0) ? 0 : ((hash) >> (64 - (n))))
264922028508SToomas Soome static int
zap_deref_leaf(fat_zap_t * zap,uint64_t h,zap_leaf_t ** lp)265022028508SToomas Soome zap_deref_leaf(fat_zap_t *zap, uint64_t h, zap_leaf_t **lp)
265122028508SToomas Soome {
265222028508SToomas Soome 	uint64_t idx, blk;
265322028508SToomas Soome 	int err;
265422028508SToomas Soome 
265522028508SToomas Soome 	idx = ZAP_HASH_IDX(h, zap->zap_phys->zap_ptrtbl.zt_shift);
265622028508SToomas Soome 	err = zap_idx_to_blk(zap, idx, &blk);
265722028508SToomas Soome 	if (err != 0)
265822028508SToomas Soome 		return (err);
265922028508SToomas Soome 	return (zap_get_leaf_byblk(zap, blk, lp));
266022028508SToomas Soome }
266122028508SToomas Soome 
266222028508SToomas Soome #define	CHAIN_END	0xffff	/* end of the chunk chain */
266322028508SToomas Soome #define	LEAF_HASH(l, h) \
266422028508SToomas Soome 	((ZAP_LEAF_HASH_NUMENTRIES(l)-1) & \
266522028508SToomas Soome 	((h) >> \
266622028508SToomas Soome 	(64 - ZAP_LEAF_HASH_SHIFT(l) - (l)->l_phys->l_hdr.lh_prefix_len)))
266722028508SToomas Soome #define	LEAF_HASH_ENTPTR(l, h)	(&(l)->l_phys->l_hash[LEAF_HASH(l, h)])
266822028508SToomas Soome 
266922028508SToomas Soome static int
zap_leaf_lookup(zap_leaf_t * zl,uint64_t hash,const char * name,uint64_t integer_size,uint64_t num_integers,void * value)267022028508SToomas Soome zap_leaf_lookup(zap_leaf_t *zl, uint64_t hash, const char *name,
267122028508SToomas Soome     uint64_t integer_size, uint64_t num_integers, void *value)
267222028508SToomas Soome {
267322028508SToomas Soome 	int rc;
267422028508SToomas Soome 	uint16_t *chunkp;
267522028508SToomas Soome 	struct zap_leaf_entry *le;
267622028508SToomas Soome 
267722028508SToomas Soome 	/*
267822028508SToomas Soome 	 * Make sure this chunk matches our hash.
267922028508SToomas Soome 	 */
268022028508SToomas Soome 	if (zl->l_phys->l_hdr.lh_prefix_len > 0 &&
268122028508SToomas Soome 	    zl->l_phys->l_hdr.lh_prefix !=
268222028508SToomas Soome 	    hash >> (64 - zl->l_phys->l_hdr.lh_prefix_len))
268322028508SToomas Soome 		return (EIO);
268422028508SToomas Soome 
268522028508SToomas Soome 	rc = ENOENT;
268622028508SToomas Soome 	for (chunkp = LEAF_HASH_ENTPTR(zl, hash);
268722028508SToomas Soome 	    *chunkp != CHAIN_END; chunkp = &le->le_next) {
268822028508SToomas Soome 		zap_leaf_chunk_t *zc;
268922028508SToomas Soome 		uint16_t chunk = *chunkp;
269022028508SToomas Soome 
269122028508SToomas Soome 		le = ZAP_LEAF_ENTRY(zl, chunk);
269222028508SToomas Soome 		if (le->le_hash != hash)
269322028508SToomas Soome 			continue;
269422028508SToomas Soome 		zc = &ZAP_LEAF_CHUNK(zl, chunk);
269522028508SToomas Soome 		if (fzap_name_equal(zl, zc, name)) {
269622028508SToomas Soome 			if (zc->l_entry.le_value_intlen > integer_size) {
269722028508SToomas Soome 				rc = EINVAL;
269822028508SToomas Soome 			} else {
269922028508SToomas Soome 				fzap_leaf_array(zl, zc, integer_size,
270022028508SToomas Soome 				    num_integers, value);
270122028508SToomas Soome 				rc = 0;
270222028508SToomas Soome 			}
270322028508SToomas Soome 			break;
270422028508SToomas Soome 		}
270522028508SToomas Soome 	}
270622028508SToomas Soome 	return (rc);
270722028508SToomas Soome }
270822028508SToomas Soome 
270922028508SToomas Soome /*
271022028508SToomas Soome  * Lookup a value in a fatzap directory.
271122028508SToomas Soome  */
271222028508SToomas Soome static int
fzap_lookup(const spa_t * spa,const dnode_phys_t * dnode,zap_phys_t * zh,const char * name,uint64_t integer_size,uint64_t num_integers,void * value)271322028508SToomas Soome fzap_lookup(const spa_t *spa, const dnode_phys_t *dnode, zap_phys_t *zh,
271422028508SToomas Soome     const char *name, uint64_t integer_size, uint64_t num_integers,
271522028508SToomas Soome     void *value)
271622028508SToomas Soome {
271722028508SToomas Soome 	int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
271822028508SToomas Soome 	fat_zap_t z;
271922028508SToomas Soome 	zap_leaf_t *zl;
272022028508SToomas Soome 	uint64_t hash;
272122028508SToomas Soome 	int rc;
272222028508SToomas Soome 
272322028508SToomas Soome 	if (zh->zap_magic != ZAP_MAGIC)
272422028508SToomas Soome 		return (EIO);
272522028508SToomas Soome 
272622028508SToomas Soome 	if ((rc = fzap_check_size(integer_size, num_integers)) != 0)
272722028508SToomas Soome 		return (rc);
272822028508SToomas Soome 
272922028508SToomas Soome 	z.zap_block_shift = ilog2(bsize);
273022028508SToomas Soome 	z.zap_phys = zh;
273122028508SToomas Soome 	z.zap_spa = spa;
273222028508SToomas Soome 	z.zap_dnode = dnode;
273322028508SToomas Soome 
273422028508SToomas Soome 	hash = zap_hash(zh->zap_salt, name);
273522028508SToomas Soome 	rc = zap_deref_leaf(&z, hash, &zl);
273622028508SToomas Soome 	if (rc != 0)
273722028508SToomas Soome 		return (rc);
273822028508SToomas Soome 
273922028508SToomas Soome 	rc = zap_leaf_lookup(zl, hash, name, integer_size, num_integers, value);
274022028508SToomas Soome 
274122028508SToomas Soome 	zap_leaf_free(zl);
274222028508SToomas Soome 	return (rc);
274322028508SToomas Soome }
274422028508SToomas Soome 
274522028508SToomas Soome /*
274622028508SToomas Soome  * Lookup a name in a zap object and return its value as a uint64_t.
274722028508SToomas Soome  */
274822028508SToomas Soome static int
zap_lookup(const spa_t * spa,const dnode_phys_t * dnode,const char * name,uint64_t integer_size,uint64_t num_integers,void * value)274922028508SToomas Soome zap_lookup(const spa_t *spa, const dnode_phys_t *dnode, const char *name,
275022028508SToomas Soome     uint64_t integer_size, uint64_t num_integers, void *value)
275122028508SToomas Soome {
275222028508SToomas Soome 	int rc;
275322028508SToomas Soome 	zap_phys_t *zap;
275422028508SToomas Soome 	size_t size = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
275522028508SToomas Soome 
275622028508SToomas Soome 	zap = malloc(size);
275722028508SToomas Soome 	if (zap == NULL)
275822028508SToomas Soome 		return (ENOMEM);
275922028508SToomas Soome 
276022028508SToomas Soome 	rc = dnode_read(spa, dnode, 0, zap, size);
276122028508SToomas Soome 	if (rc)
276222028508SToomas Soome 		goto done;
276322028508SToomas Soome 
276422028508SToomas Soome 	switch (zap->zap_block_type) {
276522028508SToomas Soome 	case ZBT_MICRO:
276622028508SToomas Soome 		rc = mzap_lookup((const mzap_phys_t *)zap, size, name, value);
276722028508SToomas Soome 		break;
276822028508SToomas Soome 	case ZBT_HEADER:
276922028508SToomas Soome 		rc = fzap_lookup(spa, dnode, zap, name, integer_size,
277022028508SToomas Soome 		    num_integers, value);
277122028508SToomas Soome 		break;
277222028508SToomas Soome 	default:
277322028508SToomas Soome 		printf("ZFS: invalid zap_type=%" PRIx64 "\n",
277422028508SToomas Soome 		    zap->zap_block_type);
277522028508SToomas Soome 		rc = EIO;
277622028508SToomas Soome 	}
277722028508SToomas Soome done:
277822028508SToomas Soome 	free(zap);
277922028508SToomas Soome 	return (rc);
278022028508SToomas Soome }
278122028508SToomas Soome 
278222028508SToomas Soome /*
278322028508SToomas Soome  * List a microzap directory.
278422028508SToomas Soome  */
278522028508SToomas Soome static int
mzap_list(const mzap_phys_t * mz,size_t size,int (* callback)(const char *,uint64_t))278622028508SToomas Soome mzap_list(const mzap_phys_t *mz, size_t size,
278722028508SToomas Soome     int (*callback)(const char *, uint64_t))
278822028508SToomas Soome {
278922028508SToomas Soome 	const mzap_ent_phys_t *mze;
279022028508SToomas Soome 	int chunks, i, rc;
279122028508SToomas Soome 
279222028508SToomas Soome 	/*
279322028508SToomas Soome 	 * Microzap objects use exactly one block. Read the whole
279422028508SToomas Soome 	 * thing.
279522028508SToomas Soome 	 */
279622028508SToomas Soome 	rc = 0;
279722028508SToomas Soome 	chunks = size / MZAP_ENT_LEN - 1;
279822028508SToomas Soome 	for (i = 0; i < chunks; i++) {
279922028508SToomas Soome 		mze = &mz->mz_chunk[i];
280022028508SToomas Soome 		if (mze->mze_name[0]) {
280122028508SToomas Soome 			rc = callback(mze->mze_name, mze->mze_value);
280222028508SToomas Soome 			if (rc != 0)
280322028508SToomas Soome 				break;
280422028508SToomas Soome 		}
280522028508SToomas Soome 	}
280622028508SToomas Soome 
280722028508SToomas Soome 	return (rc);
280822028508SToomas Soome }
280922028508SToomas Soome 
281022028508SToomas Soome /*
281122028508SToomas Soome  * List a fatzap directory.
281222028508SToomas Soome  */
281322028508SToomas Soome static int
fzap_list(const spa_t * spa,const dnode_phys_t * dnode,zap_phys_t * zh,int (* callback)(const char *,uint64_t))281422028508SToomas Soome fzap_list(const spa_t *spa, const dnode_phys_t *dnode, zap_phys_t *zh,
281522028508SToomas Soome     int (*callback)(const char *, uint64_t))
281622028508SToomas Soome {
281722028508SToomas Soome 	int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
281822028508SToomas Soome 	fat_zap_t z;
281922028508SToomas Soome 	int i, j, rc;
282022028508SToomas Soome 
282122028508SToomas Soome 	if (zh->zap_magic != ZAP_MAGIC)
282222028508SToomas Soome 		return (EIO);
282322028508SToomas Soome 
282422028508SToomas Soome 	z.zap_block_shift = ilog2(bsize);
282522028508SToomas Soome 	z.zap_phys = zh;
282622028508SToomas Soome 
282722028508SToomas Soome 	/*
282822028508SToomas Soome 	 * This assumes that the leaf blocks start at block 1. The
282922028508SToomas Soome 	 * documentation isn't exactly clear on this.
283022028508SToomas Soome 	 */
283122028508SToomas Soome 	zap_leaf_t zl;
283222028508SToomas Soome 	zl.l_bs = z.zap_block_shift;
283322028508SToomas Soome 	zl.l_phys = malloc(bsize);
283422028508SToomas Soome 	if (zl.l_phys == NULL)
283522028508SToomas Soome 		return (ENOMEM);
283622028508SToomas Soome 
283722028508SToomas Soome 	for (i = 0; i < zh->zap_num_leafs; i++) {
283822028508SToomas Soome 		off_t off = ((off_t)(i + 1)) << zl.l_bs;
283922028508SToomas Soome 		char name[256], *p;
284022028508SToomas Soome 		uint64_t value;
284122028508SToomas Soome 
284222028508SToomas Soome 		if (dnode_read(spa, dnode, off, zl.l_phys, bsize)) {
284322028508SToomas Soome 			free(zl.l_phys);
284422028508SToomas Soome 			return (EIO);
284522028508SToomas Soome 		}
284622028508SToomas Soome 
284722028508SToomas Soome 		for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) {
284822028508SToomas Soome 			zap_leaf_chunk_t *zc, *nc;
284922028508SToomas Soome 			int namelen;
285022028508SToomas Soome 
285122028508SToomas Soome 			zc = &ZAP_LEAF_CHUNK(&zl, j);
285222028508SToomas Soome 			if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
285322028508SToomas Soome 				continue;
285422028508SToomas Soome 			namelen = zc->l_entry.le_name_numints;
285522028508SToomas Soome 			if (namelen > sizeof (name))
285622028508SToomas Soome 				namelen = sizeof (name);
285722028508SToomas Soome 
285822028508SToomas Soome 			/*
285922028508SToomas Soome 			 * Paste the name back together.
286022028508SToomas Soome 			 */
286122028508SToomas Soome 			nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk);
286222028508SToomas Soome 			p = name;
286322028508SToomas Soome 			while (namelen > 0) {
286422028508SToomas Soome 				int len;
286522028508SToomas Soome 				len = namelen;
286622028508SToomas Soome 				if (len > ZAP_LEAF_ARRAY_BYTES)
286722028508SToomas Soome 					len = ZAP_LEAF_ARRAY_BYTES;
286822028508SToomas Soome 				memcpy(p, nc->l_array.la_array, len);
286922028508SToomas Soome 				p += len;
287022028508SToomas Soome 				namelen -= len;
287122028508SToomas Soome 				nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next);
287222028508SToomas Soome 			}
287322028508SToomas Soome 
287422028508SToomas Soome 			/*
287522028508SToomas Soome 			 * Assume the first eight bytes of the value are
287622028508SToomas Soome 			 * a uint64_t.
287722028508SToomas Soome 			 */
287822028508SToomas Soome 			value = fzap_leaf_value(&zl, zc);
287922028508SToomas Soome 
288022028508SToomas Soome 			/* printf("%s 0x%jx\n", name, (uintmax_t)value); */
288122028508SToomas Soome 			rc = callback((const char *)name, value);
288222028508SToomas Soome 			if (rc != 0) {
288322028508SToomas Soome 				free(zl.l_phys);
288422028508SToomas Soome 				return (rc);
288522028508SToomas Soome 			}
288622028508SToomas Soome 		}
288722028508SToomas Soome 	}
288822028508SToomas Soome 
288922028508SToomas Soome 	free(zl.l_phys);
289022028508SToomas Soome 	return (0);
289122028508SToomas Soome }
289222028508SToomas Soome 
zfs_printf(const char * name,uint64_t value __unused)289322028508SToomas Soome static int zfs_printf(const char *name, uint64_t value __unused)
289422028508SToomas Soome {
289522028508SToomas Soome 
289622028508SToomas Soome 	printf("%s\n", name);
289722028508SToomas Soome 
289822028508SToomas Soome 	return (0);
289922028508SToomas Soome }
290022028508SToomas Soome 
290122028508SToomas Soome /*
290222028508SToomas Soome  * List a zap directory.
290322028508SToomas Soome  */
290422028508SToomas Soome static int
zap_list(const spa_t * spa,const dnode_phys_t * dnode)290522028508SToomas Soome zap_list(const spa_t *spa, const dnode_phys_t *dnode)
290622028508SToomas Soome {
290722028508SToomas Soome 	zap_phys_t *zap;
290822028508SToomas Soome 	size_t size = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
290922028508SToomas Soome 	int rc;
291022028508SToomas Soome 
291122028508SToomas Soome 	zap = malloc(size);
291222028508SToomas Soome 	if (zap == NULL)
291322028508SToomas Soome 		return (ENOMEM);
291422028508SToomas Soome 
291522028508SToomas Soome 	rc = dnode_read(spa, dnode, 0, zap, size);
291622028508SToomas Soome 	if (rc == 0) {
291722028508SToomas Soome 		if (zap->zap_block_type == ZBT_MICRO)
291822028508SToomas Soome 			rc = mzap_list((const mzap_phys_t *)zap, size,
291922028508SToomas Soome 			    zfs_printf);
292022028508SToomas Soome 		else
292122028508SToomas Soome 			rc = fzap_list(spa, dnode, zap, zfs_printf);
292222028508SToomas Soome 	}
292322028508SToomas Soome 	free(zap);
292422028508SToomas Soome 	return (rc);
292522028508SToomas Soome }
292622028508SToomas Soome 
292722028508SToomas Soome static int
objset_get_dnode(const spa_t * spa,const objset_phys_t * os,uint64_t objnum,dnode_phys_t * dnode)292822028508SToomas Soome objset_get_dnode(const spa_t *spa, const objset_phys_t *os, uint64_t objnum,
292922028508SToomas Soome     dnode_phys_t *dnode)
293022028508SToomas Soome {
293122028508SToomas Soome 	off_t offset;
293222028508SToomas Soome 
293322028508SToomas Soome 	offset = objnum * sizeof (dnode_phys_t);
293422028508SToomas Soome 	return (dnode_read(spa, &os->os_meta_dnode, offset,
293522028508SToomas Soome 	    dnode, sizeof (dnode_phys_t)));
293622028508SToomas Soome }
293722028508SToomas Soome 
293822028508SToomas Soome /*
293922028508SToomas Soome  * Lookup a name in a microzap directory.
294022028508SToomas Soome  */
294122028508SToomas Soome static int
mzap_rlookup(const mzap_phys_t * mz,size_t size,char * name,uint64_t value)294222028508SToomas Soome mzap_rlookup(const mzap_phys_t *mz, size_t size, char *name, uint64_t value)
294322028508SToomas Soome {
294422028508SToomas Soome 	const mzap_ent_phys_t *mze;
294522028508SToomas Soome 	int chunks, i;
294622028508SToomas Soome 
294722028508SToomas Soome 	/*
294822028508SToomas Soome 	 * Microzap objects use exactly one block. Read the whole
294922028508SToomas Soome 	 * thing.
295022028508SToomas Soome 	 */
295122028508SToomas Soome 	chunks = size / MZAP_ENT_LEN - 1;
295222028508SToomas Soome 	for (i = 0; i < chunks; i++) {
295322028508SToomas Soome 		mze = &mz->mz_chunk[i];
295422028508SToomas Soome 		if (value == mze->mze_value) {
295522028508SToomas Soome 			strcpy(name, mze->mze_name);
295622028508SToomas Soome 			return (0);
295722028508SToomas Soome 		}
295822028508SToomas Soome 	}
295922028508SToomas Soome 
296022028508SToomas Soome 	return (ENOENT);
296122028508SToomas Soome }
296222028508SToomas Soome 
296322028508SToomas Soome static void
fzap_name_copy(const zap_leaf_t * zl,const zap_leaf_chunk_t * zc,char * name)296422028508SToomas Soome fzap_name_copy(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, char *name)
296522028508SToomas Soome {
296622028508SToomas Soome 	size_t namelen;
296722028508SToomas Soome 	const zap_leaf_chunk_t *nc;
296822028508SToomas Soome 	char *p;
296922028508SToomas Soome 
297022028508SToomas Soome 	namelen = zc->l_entry.le_name_numints;
297122028508SToomas Soome 
297222028508SToomas Soome 	nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk);
297322028508SToomas Soome 	p = name;
297422028508SToomas Soome 	while (namelen > 0) {
297522028508SToomas Soome 		size_t len;
297622028508SToomas Soome 		len = namelen;
297722028508SToomas Soome 		if (len > ZAP_LEAF_ARRAY_BYTES)
297822028508SToomas Soome 			len = ZAP_LEAF_ARRAY_BYTES;
297922028508SToomas Soome 		memcpy(p, nc->l_array.la_array, len);
298022028508SToomas Soome 		p += len;
298122028508SToomas Soome 		namelen -= len;
298222028508SToomas Soome 		nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next);
298322028508SToomas Soome 	}
298422028508SToomas Soome 
298522028508SToomas Soome 	*p = '\0';
298622028508SToomas Soome }
298722028508SToomas Soome 
298822028508SToomas Soome static int
fzap_rlookup(const spa_t * spa,const dnode_phys_t * dnode,zap_phys_t * zh,char * name,uint64_t value)298922028508SToomas Soome fzap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, zap_phys_t *zh,
299022028508SToomas Soome     char *name, uint64_t value)
299122028508SToomas Soome {
299222028508SToomas Soome 	int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
299322028508SToomas Soome 	fat_zap_t z;
299422028508SToomas Soome 	uint64_t i;
299522028508SToomas Soome 	int j, rc;
299622028508SToomas Soome 
299722028508SToomas Soome 	if (zh->zap_magic != ZAP_MAGIC)
299822028508SToomas Soome 		return (EIO);
299922028508SToomas Soome 
300022028508SToomas Soome 	z.zap_block_shift = ilog2(bsize);
300122028508SToomas Soome 	z.zap_phys = zh;
300222028508SToomas Soome 
300322028508SToomas Soome 	/*
300422028508SToomas Soome 	 * This assumes that the leaf blocks start at block 1. The
300522028508SToomas Soome 	 * documentation isn't exactly clear on this.
300622028508SToomas Soome 	 */
300722028508SToomas Soome 	zap_leaf_t zl;
300822028508SToomas Soome 	zl.l_bs = z.zap_block_shift;
300922028508SToomas Soome 	zl.l_phys = malloc(bsize);
301022028508SToomas Soome 	if (zl.l_phys == NULL)
301122028508SToomas Soome 		return (ENOMEM);
301222028508SToomas Soome 
301322028508SToomas Soome 	for (i = 0; i < zh->zap_num_leafs; i++) {
301422028508SToomas Soome 		off_t off = ((off_t)(i + 1)) << zl.l_bs;
301522028508SToomas Soome 
301622028508SToomas Soome 		rc = dnode_read(spa, dnode, off, zl.l_phys, bsize);
301722028508SToomas Soome 		if (rc != 0)
301822028508SToomas Soome 			goto done;
301922028508SToomas Soome 
302022028508SToomas Soome 		for (j = 0; j < ZAP_LEAF_NUMCHUNKS(&zl); j++) {
302122028508SToomas Soome 			zap_leaf_chunk_t *zc;
302222028508SToomas Soome 
302322028508SToomas Soome 			zc = &ZAP_LEAF_CHUNK(&zl, j);
302422028508SToomas Soome 			if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY)
302522028508SToomas Soome 				continue;
302622028508SToomas Soome 			if (zc->l_entry.le_value_intlen != 8 ||
302722028508SToomas Soome 			    zc->l_entry.le_value_numints != 1)
302822028508SToomas Soome 				continue;
302922028508SToomas Soome 
303022028508SToomas Soome 			if (fzap_leaf_value(&zl, zc) == value) {
303122028508SToomas Soome 				fzap_name_copy(&zl, zc, name);
303222028508SToomas Soome 				goto done;
303322028508SToomas Soome 			}
303422028508SToomas Soome 		}
303522028508SToomas Soome 	}
303622028508SToomas Soome 
303722028508SToomas Soome 	rc = ENOENT;
303822028508SToomas Soome done:
303922028508SToomas Soome 	free(zl.l_phys);
304022028508SToomas Soome 	return (rc);
304122028508SToomas Soome }
304222028508SToomas Soome 
304322028508SToomas Soome static int
zap_rlookup(const spa_t * spa,const dnode_phys_t * dnode,char * name,uint64_t value)304422028508SToomas Soome zap_rlookup(const spa_t *spa, const dnode_phys_t *dnode, char *name,
304522028508SToomas Soome     uint64_t value)
304622028508SToomas Soome {
304722028508SToomas Soome 	zap_phys_t *zap;
304822028508SToomas Soome 	size_t size = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT;
304922028508SToomas Soome 	int rc;
305022028508SToomas Soome 
305122028508SToomas Soome 	zap = malloc(size);
305222028508SToomas Soome 	if (zap == NULL)
305322028508SToomas Soome 		return (ENOMEM);
305422028508SToomas Soome 
305522028508SToomas Soome 	rc = dnode_read(spa, dnode, 0, zap, size);
305622028508SToomas Soome 	if (rc == 0) {
305722028508SToomas Soome 		if (zap->zap_block_type == ZBT_MICRO)
305822028508SToomas Soome 			rc = mzap_rlookup((const mzap_phys_t *)zap, size,
305922028508SToomas Soome 			    name, value);
306022028508SToomas Soome 		else
306122028508SToomas Soome 			rc = fzap_rlookup(spa, dnode, zap, name, value);
306222028508SToomas Soome 	}
306322028508SToomas Soome 	free(zap);
306422028508SToomas Soome 	return (rc);
306522028508SToomas Soome }
306622028508SToomas Soome 
306722028508SToomas Soome static int
zfs_rlookup(const spa_t * spa,uint64_t objnum,char * result)306822028508SToomas Soome zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result)
306922028508SToomas Soome {
307022028508SToomas Soome 	char name[256];
307122028508SToomas Soome 	char component[256];
307222028508SToomas Soome 	uint64_t dir_obj, parent_obj, child_dir_zapobj;
307322028508SToomas Soome 	dnode_phys_t child_dir_zap, dataset, dir, parent;
307422028508SToomas Soome 	dsl_dir_phys_t *dd;
307522028508SToomas Soome 	dsl_dataset_phys_t *ds;
307622028508SToomas Soome 	char *p;
307722028508SToomas Soome 	int len;
307822028508SToomas Soome 
307922028508SToomas Soome 	p = &name[sizeof (name) - 1];
308022028508SToomas Soome 	*p = '\0';
308122028508SToomas Soome 
308222028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
308322028508SToomas Soome 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
308422028508SToomas Soome 		return (EIO);
308522028508SToomas Soome 	}
308622028508SToomas Soome 	ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
308722028508SToomas Soome 	dir_obj = ds->ds_dir_obj;
308822028508SToomas Soome 
308922028508SToomas Soome 	for (;;) {
309022028508SToomas Soome 		if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir) != 0)
309122028508SToomas Soome 			return (EIO);
309222028508SToomas Soome 		dd = (dsl_dir_phys_t *)&dir.dn_bonus;
309322028508SToomas Soome 
309422028508SToomas Soome 		/* Actual loop condition. */
309522028508SToomas Soome 		parent_obj = dd->dd_parent_obj;
309622028508SToomas Soome 		if (parent_obj == 0)
309722028508SToomas Soome 			break;
309822028508SToomas Soome 
309922028508SToomas Soome 		if (objset_get_dnode(spa, &spa->spa_mos, parent_obj,
310022028508SToomas Soome 		    &parent) != 0)
310122028508SToomas Soome 			return (EIO);
310222028508SToomas Soome 		dd = (dsl_dir_phys_t *)&parent.dn_bonus;
310322028508SToomas Soome 		child_dir_zapobj = dd->dd_child_dir_zapobj;
310422028508SToomas Soome 		if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
310522028508SToomas Soome 		    &child_dir_zap) != 0)
310622028508SToomas Soome 			return (EIO);
310722028508SToomas Soome 		if (zap_rlookup(spa, &child_dir_zap, component, dir_obj) != 0)
310822028508SToomas Soome 			return (EIO);
310922028508SToomas Soome 
311022028508SToomas Soome 		len = strlen(component);
311122028508SToomas Soome 		p -= len;
311222028508SToomas Soome 		memcpy(p, component, len);
311322028508SToomas Soome 		--p;
311422028508SToomas Soome 		*p = '/';
311522028508SToomas Soome 
311622028508SToomas Soome 		/* Actual loop iteration. */
311722028508SToomas Soome 		dir_obj = parent_obj;
311822028508SToomas Soome 	}
311922028508SToomas Soome 
312022028508SToomas Soome 	if (*p != '\0')
312122028508SToomas Soome 		++p;
312222028508SToomas Soome 	strcpy(result, p);
312322028508SToomas Soome 
312422028508SToomas Soome 	return (0);
312522028508SToomas Soome }
312622028508SToomas Soome 
312722028508SToomas Soome static int
zfs_lookup_dataset(const spa_t * spa,const char * name,uint64_t * objnum)312822028508SToomas Soome zfs_lookup_dataset(const spa_t *spa, const char *name, uint64_t *objnum)
312922028508SToomas Soome {
313022028508SToomas Soome 	char element[256];
313122028508SToomas Soome 	uint64_t dir_obj, child_dir_zapobj;
313222028508SToomas Soome 	dnode_phys_t child_dir_zap, dir;
313322028508SToomas Soome 	dsl_dir_phys_t *dd;
313422028508SToomas Soome 	const char *p, *q;
313522028508SToomas Soome 
313622028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos,
313722028508SToomas Soome 	    DMU_POOL_DIRECTORY_OBJECT, &dir))
313822028508SToomas Soome 		return (EIO);
313922028508SToomas Soome 	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, sizeof (dir_obj),
314022028508SToomas Soome 	    1, &dir_obj))
314122028508SToomas Soome 		return (EIO);
314222028508SToomas Soome 
314322028508SToomas Soome 	p = name;
314422028508SToomas Soome 	for (;;) {
314522028508SToomas Soome 		if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir))
314622028508SToomas Soome 			return (EIO);
314722028508SToomas Soome 		dd = (dsl_dir_phys_t *)&dir.dn_bonus;
314822028508SToomas Soome 
314922028508SToomas Soome 		while (*p == '/')
315022028508SToomas Soome 			p++;
315122028508SToomas Soome 		/* Actual loop condition #1. */
315222028508SToomas Soome 		if (*p == '\0')
315322028508SToomas Soome 			break;
315422028508SToomas Soome 
315522028508SToomas Soome 		q = strchr(p, '/');
315622028508SToomas Soome 		if (q) {
315722028508SToomas Soome 			memcpy(element, p, q - p);
315822028508SToomas Soome 			element[q - p] = '\0';
315922028508SToomas Soome 			p = q + 1;
316022028508SToomas Soome 		} else {
316122028508SToomas Soome 			strcpy(element, p);
316222028508SToomas Soome 			p += strlen(p);
316322028508SToomas Soome 		}
316422028508SToomas Soome 
316522028508SToomas Soome 		child_dir_zapobj = dd->dd_child_dir_zapobj;
316622028508SToomas Soome 		if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
316722028508SToomas Soome 		    &child_dir_zap) != 0)
316822028508SToomas Soome 			return (EIO);
316922028508SToomas Soome 
317022028508SToomas Soome 		/* Actual loop condition #2. */
317122028508SToomas Soome 		if (zap_lookup(spa, &child_dir_zap, element, sizeof (dir_obj),
317222028508SToomas Soome 		    1, &dir_obj) != 0)
317322028508SToomas Soome 			return (ENOENT);
317422028508SToomas Soome 	}
317522028508SToomas Soome 
317622028508SToomas Soome 	*objnum = dd->dd_head_dataset_obj;
317722028508SToomas Soome 	return (0);
317822028508SToomas Soome }
317922028508SToomas Soome 
318022028508SToomas Soome #pragma GCC diagnostic ignored "-Wstrict-aliasing"
318122028508SToomas Soome static int
zfs_list_dataset(const spa_t * spa,uint64_t objnum)318222028508SToomas Soome zfs_list_dataset(const spa_t *spa, uint64_t objnum)
318322028508SToomas Soome {
318422028508SToomas Soome 	uint64_t dir_obj, child_dir_zapobj;
318522028508SToomas Soome 	dnode_phys_t child_dir_zap, dir, dataset;
318622028508SToomas Soome 	dsl_dataset_phys_t *ds;
318722028508SToomas Soome 	dsl_dir_phys_t *dd;
318822028508SToomas Soome 
318922028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
319022028508SToomas Soome 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
319122028508SToomas Soome 		return (EIO);
319222028508SToomas Soome 	}
319322028508SToomas Soome 	ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
319422028508SToomas Soome 	dir_obj = ds->ds_dir_obj;
319522028508SToomas Soome 
319622028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) {
319722028508SToomas Soome 		printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
319822028508SToomas Soome 		return (EIO);
319922028508SToomas Soome 	}
320022028508SToomas Soome 	dd = (dsl_dir_phys_t *)&dir.dn_bonus;
320122028508SToomas Soome 
320222028508SToomas Soome 	child_dir_zapobj = dd->dd_child_dir_zapobj;
320322028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
320422028508SToomas Soome 	    &child_dir_zap) != 0) {
320522028508SToomas Soome 		printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
320622028508SToomas Soome 		return (EIO);
320722028508SToomas Soome 	}
320822028508SToomas Soome 
320922028508SToomas Soome 	return (zap_list(spa, &child_dir_zap) != 0);
321022028508SToomas Soome }
321122028508SToomas Soome 
321222028508SToomas Soome int
zfs_callback_dataset(const spa_t * spa,uint64_t objnum,int (* callback)(const char *,uint64_t))321322028508SToomas Soome zfs_callback_dataset(const spa_t *spa, uint64_t objnum,
321422028508SToomas Soome     int (*callback)(const char *, uint64_t))
321522028508SToomas Soome {
321622028508SToomas Soome 	uint64_t dir_obj, child_dir_zapobj;
321722028508SToomas Soome 	dnode_phys_t child_dir_zap, dir, dataset;
321822028508SToomas Soome 	dsl_dataset_phys_t *ds;
321922028508SToomas Soome 	dsl_dir_phys_t *dd;
322022028508SToomas Soome 	zap_phys_t *zap;
322122028508SToomas Soome 	size_t size;
322222028508SToomas Soome 	int err;
322322028508SToomas Soome 
322422028508SToomas Soome 	err = objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset);
322522028508SToomas Soome 	if (err != 0) {
322622028508SToomas Soome 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
322722028508SToomas Soome 		return (err);
322822028508SToomas Soome 	}
322922028508SToomas Soome 	ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
323022028508SToomas Soome 	dir_obj = ds->ds_dir_obj;
323122028508SToomas Soome 
323222028508SToomas Soome 	err = objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir);
323322028508SToomas Soome 	if (err != 0) {
323422028508SToomas Soome 		printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
323522028508SToomas Soome 		return (err);
323622028508SToomas Soome 	}
323722028508SToomas Soome 	dd = (dsl_dir_phys_t *)&dir.dn_bonus;
323822028508SToomas Soome 
323922028508SToomas Soome 	child_dir_zapobj = dd->dd_child_dir_zapobj;
324022028508SToomas Soome 	err = objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
324122028508SToomas Soome 	    &child_dir_zap);
324222028508SToomas Soome 	if (err != 0) {
324322028508SToomas Soome 		printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
324422028508SToomas Soome 		return (err);
324522028508SToomas Soome 	}
324622028508SToomas Soome 
324722028508SToomas Soome 	size = child_dir_zap.dn_datablkszsec << SPA_MINBLOCKSHIFT;
324822028508SToomas Soome 	zap = malloc(size);
324922028508SToomas Soome 	if (zap != NULL) {
325022028508SToomas Soome 		err = dnode_read(spa, &child_dir_zap, 0, zap, size);
325122028508SToomas Soome 		if (err != 0)
325222028508SToomas Soome 			goto done;
325322028508SToomas Soome 
325422028508SToomas Soome 		if (zap->zap_block_type == ZBT_MICRO)
325522028508SToomas Soome 			err = mzap_list((const mzap_phys_t *)zap, size,
325622028508SToomas Soome 			    callback);
325722028508SToomas Soome 		else
325822028508SToomas Soome 			err = fzap_list(spa, &child_dir_zap, zap, callback);
325922028508SToomas Soome 	} else {
326022028508SToomas Soome 		err = ENOMEM;
326122028508SToomas Soome 	}
326222028508SToomas Soome done:
326322028508SToomas Soome 	free(zap);
326422028508SToomas Soome 	return (err);
326522028508SToomas Soome }
326622028508SToomas Soome 
326722028508SToomas Soome /*
326822028508SToomas Soome  * Find the object set given the object number of its dataset object
326922028508SToomas Soome  * and return its details in *objset
327022028508SToomas Soome  */
327122028508SToomas Soome static int
zfs_mount_dataset(const spa_t * spa,uint64_t objnum,objset_phys_t * objset)327222028508SToomas Soome zfs_mount_dataset(const spa_t *spa, uint64_t objnum, objset_phys_t *objset)
327322028508SToomas Soome {
327422028508SToomas Soome 	dnode_phys_t dataset;
327522028508SToomas Soome 	dsl_dataset_phys_t *ds;
327622028508SToomas Soome 
327722028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
327822028508SToomas Soome 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
327922028508SToomas Soome 		return (EIO);
328022028508SToomas Soome 	}
328122028508SToomas Soome 
328222028508SToomas Soome 	ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
328322028508SToomas Soome 	if (zio_read(spa, &ds->ds_bp, objset)) {
328422028508SToomas Soome 		printf("ZFS: can't read object set for dataset %ju\n",
328522028508SToomas Soome 		    (uintmax_t)objnum);
328622028508SToomas Soome 		return (EIO);
328722028508SToomas Soome 	}
328822028508SToomas Soome 
328922028508SToomas Soome 	return (0);
329022028508SToomas Soome }
329122028508SToomas Soome 
329222028508SToomas Soome /*
329322028508SToomas Soome  * Find the object set pointed to by the BOOTFS property or the root
329422028508SToomas Soome  * dataset if there is none and return its details in *objset
329522028508SToomas Soome  */
329622028508SToomas Soome static int
zfs_get_root(const spa_t * spa,uint64_t * objid)329722028508SToomas Soome zfs_get_root(const spa_t *spa, uint64_t *objid)
329822028508SToomas Soome {
329922028508SToomas Soome 	dnode_phys_t dir, propdir;
330022028508SToomas Soome 	uint64_t props, bootfs, root;
330122028508SToomas Soome 
330222028508SToomas Soome 	*objid = 0;
330322028508SToomas Soome 
330422028508SToomas Soome 	/*
330522028508SToomas Soome 	 * Start with the MOS directory object.
330622028508SToomas Soome 	 */
330722028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos,
330822028508SToomas Soome 	    DMU_POOL_DIRECTORY_OBJECT, &dir)) {
330922028508SToomas Soome 		printf("ZFS: can't read MOS object directory\n");
331022028508SToomas Soome 		return (EIO);
331122028508SToomas Soome 	}
331222028508SToomas Soome 
331322028508SToomas Soome 	/*
331422028508SToomas Soome 	 * Lookup the pool_props and see if we can find a bootfs.
331522028508SToomas Soome 	 */
331622028508SToomas Soome 	if (zap_lookup(spa, &dir, DMU_POOL_PROPS,
331722028508SToomas Soome 	    sizeof (props), 1, &props) == 0 &&
331822028508SToomas Soome 	    objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0 &&
331922028508SToomas Soome 	    zap_lookup(spa, &propdir, "bootfs",
332022028508SToomas Soome 	    sizeof (bootfs), 1, &bootfs) == 0 && bootfs != 0) {
332122028508SToomas Soome 		*objid = bootfs;
332222028508SToomas Soome 		return (0);
332322028508SToomas Soome 	}
332422028508SToomas Soome 	/*
332522028508SToomas Soome 	 * Lookup the root dataset directory
332622028508SToomas Soome 	 */
332722028508SToomas Soome 	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET,
332822028508SToomas Soome 	    sizeof (root), 1, &root) ||
332922028508SToomas Soome 	    objset_get_dnode(spa, &spa->spa_mos, root, &dir)) {
333022028508SToomas Soome 		printf("ZFS: can't find root dsl_dir\n");
333122028508SToomas Soome 		return (EIO);
333222028508SToomas Soome 	}
333322028508SToomas Soome 
333422028508SToomas Soome 	/*
333522028508SToomas Soome 	 * Use the information from the dataset directory's bonus buffer
333622028508SToomas Soome 	 * to find the dataset object and from that the object set itself.
333722028508SToomas Soome 	 */
333822028508SToomas Soome 	dsl_dir_phys_t *dd = (dsl_dir_phys_t *)&dir.dn_bonus;
333922028508SToomas Soome 	*objid = dd->dd_head_dataset_obj;
334022028508SToomas Soome 	return (0);
334122028508SToomas Soome }
334222028508SToomas Soome 
334322028508SToomas Soome static int
zfs_mount(const spa_t * spa,uint64_t rootobj,struct zfsmount * mnt)334422028508SToomas Soome zfs_mount(const spa_t *spa, uint64_t rootobj, struct zfsmount *mnt)
334522028508SToomas Soome {
334622028508SToomas Soome 
334722028508SToomas Soome 	mnt->spa = spa;
334822028508SToomas Soome 
334922028508SToomas Soome 	/*
335022028508SToomas Soome 	 * Find the root object set if not explicitly provided
335122028508SToomas Soome 	 */
335222028508SToomas Soome 	if (rootobj == 0 && zfs_get_root(spa, &rootobj)) {
335322028508SToomas Soome 		printf("ZFS: can't find root filesystem\n");
335422028508SToomas Soome 		return (EIO);
335522028508SToomas Soome 	}
335622028508SToomas Soome 
335722028508SToomas Soome 	if (zfs_mount_dataset(spa, rootobj, &mnt->objset)) {
335822028508SToomas Soome 		printf("ZFS: can't open root filesystem\n");
335922028508SToomas Soome 		return (EIO);
336022028508SToomas Soome 	}
336122028508SToomas Soome 
336222028508SToomas Soome 	mnt->rootobj = rootobj;
336322028508SToomas Soome 
336422028508SToomas Soome 	return (0);
336522028508SToomas Soome }
336622028508SToomas Soome 
336722028508SToomas Soome /*
336822028508SToomas Soome  * callback function for feature name checks.
336922028508SToomas Soome  */
337022028508SToomas Soome static int
check_feature(const char * name,uint64_t value)337122028508SToomas Soome check_feature(const char *name, uint64_t value)
337222028508SToomas Soome {
337322028508SToomas Soome 	int i;
337422028508SToomas Soome 
337522028508SToomas Soome 	if (value == 0)
337622028508SToomas Soome 		return (0);
337722028508SToomas Soome 	if (name[0] == '\0')
337822028508SToomas Soome 		return (0);
337922028508SToomas Soome 
338022028508SToomas Soome 	for (i = 0; features_for_read[i] != NULL; i++) {
338122028508SToomas Soome 		if (strcmp(name, features_for_read[i]) == 0)
338222028508SToomas Soome 			return (0);
338322028508SToomas Soome 	}
338422028508SToomas Soome 	printf("ZFS: unsupported feature: %s\n", name);
338522028508SToomas Soome 	return (EIO);
338622028508SToomas Soome }
338722028508SToomas Soome 
338822028508SToomas Soome /*
338922028508SToomas Soome  * Checks whether the MOS features that are active are supported.
339022028508SToomas Soome  */
339122028508SToomas Soome static int
check_mos_features(const spa_t * spa)339222028508SToomas Soome check_mos_features(const spa_t *spa)
339322028508SToomas Soome {
339422028508SToomas Soome 	dnode_phys_t dir;
339522028508SToomas Soome 	zap_phys_t *zap;
339622028508SToomas Soome 	uint64_t objnum;
339722028508SToomas Soome 	size_t size;
339822028508SToomas Soome 	int rc;
339922028508SToomas Soome 
340022028508SToomas Soome 	if ((rc = objset_get_dnode(spa, &spa->spa_mos, DMU_OT_OBJECT_DIRECTORY,
340122028508SToomas Soome 	    &dir)) != 0)
340222028508SToomas Soome 		return (rc);
340322028508SToomas Soome 	if ((rc = zap_lookup(spa, &dir, DMU_POOL_FEATURES_FOR_READ,
340422028508SToomas Soome 	    sizeof (objnum), 1, &objnum)) != 0) {
340522028508SToomas Soome 		/*
340622028508SToomas Soome 		 * It is older pool without features. As we have already
340722028508SToomas Soome 		 * tested the label, just return without raising the error.
340822028508SToomas Soome 		 */
340922028508SToomas Soome 		if (rc == ENOENT)
341022028508SToomas Soome 			rc = 0;
341122028508SToomas Soome 		return (rc);
341222028508SToomas Soome 	}
341322028508SToomas Soome 
341422028508SToomas Soome 	if ((rc = objset_get_dnode(spa, &spa->spa_mos, objnum, &dir)) != 0)
341522028508SToomas Soome 		return (rc);
341622028508SToomas Soome 
341722028508SToomas Soome 	if (dir.dn_type != DMU_OTN_ZAP_METADATA)
341822028508SToomas Soome 		return (EIO);
341922028508SToomas Soome 
342022028508SToomas Soome 	size = dir.dn_datablkszsec << SPA_MINBLOCKSHIFT;
342122028508SToomas Soome 	zap = malloc(size);
342222028508SToomas Soome 	if (zap == NULL)
342322028508SToomas Soome 		return (ENOMEM);
342422028508SToomas Soome 
342522028508SToomas Soome 	if (dnode_read(spa, &dir, 0, zap, size)) {
342622028508SToomas Soome 		free(zap);
342722028508SToomas Soome 		return (EIO);
342822028508SToomas Soome 	}
342922028508SToomas Soome 
343022028508SToomas Soome 	if (zap->zap_block_type == ZBT_MICRO)
343122028508SToomas Soome 		rc = mzap_list((const mzap_phys_t *)zap, size, check_feature);
343222028508SToomas Soome 	else
343322028508SToomas Soome 		rc = fzap_list(spa, &dir, zap, check_feature);
343422028508SToomas Soome 
343522028508SToomas Soome 	free(zap);
343622028508SToomas Soome 	return (rc);
343722028508SToomas Soome }
343822028508SToomas Soome 
343922028508SToomas Soome static int
load_nvlist(spa_t * spa,uint64_t obj,nvlist_t ** value)344022028508SToomas Soome load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value)
344122028508SToomas Soome {
344222028508SToomas Soome 	dnode_phys_t dir;
344322028508SToomas Soome 	size_t size;
344422028508SToomas Soome 	int rc;
344522028508SToomas Soome 	char *nv;
344622028508SToomas Soome 
344722028508SToomas Soome 	*value = NULL;
344822028508SToomas Soome 	if ((rc = objset_get_dnode(spa, &spa->spa_mos, obj, &dir)) != 0)
344922028508SToomas Soome 		return (rc);
345022028508SToomas Soome 	if (dir.dn_type != DMU_OT_PACKED_NVLIST &&
345122028508SToomas Soome 	    dir.dn_bonustype != DMU_OT_PACKED_NVLIST_SIZE) {
345222028508SToomas Soome 		return (EIO);
345322028508SToomas Soome 	}
345422028508SToomas Soome 
345522028508SToomas Soome 	if (dir.dn_bonuslen != sizeof (uint64_t))
345622028508SToomas Soome 		return (EIO);
345722028508SToomas Soome 
345822028508SToomas Soome 	size = *(uint64_t *)DN_BONUS(&dir);
345922028508SToomas Soome 	nv = malloc(size);
346022028508SToomas Soome 	if (nv == NULL)
346122028508SToomas Soome 		return (ENOMEM);
346222028508SToomas Soome 
346322028508SToomas Soome 	rc = dnode_read(spa, &dir, 0, nv, size);
346422028508SToomas Soome 	if (rc != 0) {
346522028508SToomas Soome 		free(nv);
346622028508SToomas Soome 		nv = NULL;
346722028508SToomas Soome 		return (rc);
346822028508SToomas Soome 	}
346922028508SToomas Soome 	*value = nvlist_import(nv, size);
347022028508SToomas Soome 	free(nv);
347122028508SToomas Soome 	return (rc);
347222028508SToomas Soome }
347322028508SToomas Soome 
347422028508SToomas Soome static int
zfs_spa_init(spa_t * spa)347522028508SToomas Soome zfs_spa_init(spa_t *spa)
347622028508SToomas Soome {
347722028508SToomas Soome 	dnode_phys_t dir;
347822028508SToomas Soome 	uint64_t config_object;
347922028508SToomas Soome 	nvlist_t *nvlist;
348022028508SToomas Soome 	int rc;
348122028508SToomas Soome 
348222028508SToomas Soome 	if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
348322028508SToomas Soome 		printf("ZFS: can't read MOS of pool %s\n", spa->spa_name);
348422028508SToomas Soome 		return (EIO);
348522028508SToomas Soome 	}
348622028508SToomas Soome 	if (spa->spa_mos.os_type != DMU_OST_META) {
348722028508SToomas Soome 		printf("ZFS: corrupted MOS of pool %s\n", spa->spa_name);
348822028508SToomas Soome 		return (EIO);
348922028508SToomas Soome 	}
349022028508SToomas Soome 
349122028508SToomas Soome 	if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT,
349222028508SToomas Soome 	    &dir)) {
349322028508SToomas Soome 		printf("ZFS: failed to read pool %s directory object\n",
349422028508SToomas Soome 		    spa->spa_name);
349522028508SToomas Soome 		return (EIO);
349622028508SToomas Soome 	}
349722028508SToomas Soome 	/* this is allowed to fail, older pools do not have salt */
349822028508SToomas Soome 	rc = zap_lookup(spa, &dir, DMU_POOL_CHECKSUM_SALT, 1,
349922028508SToomas Soome 	    sizeof (spa->spa_cksum_salt.zcs_bytes),
350022028508SToomas Soome 	    spa->spa_cksum_salt.zcs_bytes);
350122028508SToomas Soome 
350222028508SToomas Soome 	rc = check_mos_features(spa);
350322028508SToomas Soome 	if (rc != 0) {
350422028508SToomas Soome 		printf("ZFS: pool %s is not supported\n", spa->spa_name);
350522028508SToomas Soome 		return (rc);
350622028508SToomas Soome 	}
350722028508SToomas Soome 
350822028508SToomas Soome 	rc = zap_lookup(spa, &dir, DMU_POOL_CONFIG,
350922028508SToomas Soome 	    sizeof (config_object), 1, &config_object);
351022028508SToomas Soome 	if (rc != 0) {
351122028508SToomas Soome 		printf("ZFS: can not read MOS %s\n", DMU_POOL_CONFIG);
351222028508SToomas Soome 		return (EIO);
351322028508SToomas Soome 	}
351422028508SToomas Soome 	rc = load_nvlist(spa, config_object, &nvlist);
351522028508SToomas Soome 	if (rc != 0)
351622028508SToomas Soome 		return (rc);
351722028508SToomas Soome 
351822028508SToomas Soome 	/*
351922028508SToomas Soome 	 * Update vdevs from MOS config. Note, we do skip encoding bytes
352022028508SToomas Soome 	 * here. See also vdev_label_read_config().
352122028508SToomas Soome 	 */
352222028508SToomas Soome 	rc = vdev_init_from_nvlist(spa, nvlist);
352322028508SToomas Soome 	nvlist_destroy(nvlist);
352422028508SToomas Soome 	return (rc);
352522028508SToomas Soome }
352622028508SToomas Soome 
352722028508SToomas Soome static int
zfs_dnode_stat(const spa_t * spa,dnode_phys_t * dn,struct stat * sb)352822028508SToomas Soome zfs_dnode_stat(const spa_t *spa, dnode_phys_t *dn, struct stat *sb)
352922028508SToomas Soome {
353022028508SToomas Soome 
353122028508SToomas Soome 	if (dn->dn_bonustype != DMU_OT_SA) {
353222028508SToomas Soome 		znode_phys_t *zp = (znode_phys_t *)dn->dn_bonus;
353322028508SToomas Soome 
353422028508SToomas Soome 		sb->st_mode = zp->zp_mode;
353522028508SToomas Soome 		sb->st_uid = zp->zp_uid;
353622028508SToomas Soome 		sb->st_gid = zp->zp_gid;
353722028508SToomas Soome 		sb->st_size = zp->zp_size;
353822028508SToomas Soome 	} else {
353922028508SToomas Soome 		sa_hdr_phys_t *sahdrp;
354022028508SToomas Soome 		int hdrsize;
354122028508SToomas Soome 		size_t size = 0;
354222028508SToomas Soome 		void *buf = NULL;
354322028508SToomas Soome 
354422028508SToomas Soome 		if (dn->dn_bonuslen != 0)
354522028508SToomas Soome 			sahdrp = (sa_hdr_phys_t *)DN_BONUS(dn);
354622028508SToomas Soome 		else {
354722028508SToomas Soome 			if ((dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR) != 0) {
354822028508SToomas Soome 				blkptr_t *bp = DN_SPILL_BLKPTR(dn);
354922028508SToomas Soome 				int error;
355022028508SToomas Soome 
355122028508SToomas Soome 				size = BP_GET_LSIZE(bp);
355222028508SToomas Soome 				buf = malloc(size);
355322028508SToomas Soome 				if (buf == NULL)
355422028508SToomas Soome 					error = ENOMEM;
355522028508SToomas Soome 				else
355622028508SToomas Soome 					error = zio_read(spa, bp, buf);
355722028508SToomas Soome 
355822028508SToomas Soome 				if (error != 0) {
355922028508SToomas Soome 					free(buf);
356022028508SToomas Soome 					return (error);
356122028508SToomas Soome 				}
356222028508SToomas Soome 				sahdrp = buf;
356322028508SToomas Soome 			} else {
356422028508SToomas Soome 				return (EIO);
356522028508SToomas Soome 			}
356622028508SToomas Soome 		}
356722028508SToomas Soome 		hdrsize = SA_HDR_SIZE(sahdrp);
356822028508SToomas Soome 		sb->st_mode = *(uint64_t *)((char *)sahdrp + hdrsize +
356922028508SToomas Soome 		    SA_MODE_OFFSET);
357022028508SToomas Soome 		sb->st_uid = *(uint64_t *)((char *)sahdrp + hdrsize +
357122028508SToomas Soome 		    SA_UID_OFFSET);
357222028508SToomas Soome 		sb->st_gid = *(uint64_t *)((char *)sahdrp + hdrsize +
357322028508SToomas Soome 		    SA_GID_OFFSET);
357422028508SToomas Soome 		sb->st_size = *(uint64_t *)((char *)sahdrp + hdrsize +
357522028508SToomas Soome 		    SA_SIZE_OFFSET);
357622028508SToomas Soome 		free(buf);
357722028508SToomas Soome 	}
357822028508SToomas Soome 
357922028508SToomas Soome 	return (0);
358022028508SToomas Soome }
358122028508SToomas Soome 
358222028508SToomas Soome static int
zfs_dnode_readlink(const spa_t * spa,dnode_phys_t * dn,char * path,size_t psize)358322028508SToomas Soome zfs_dnode_readlink(const spa_t *spa, dnode_phys_t *dn, char *path, size_t psize)
358422028508SToomas Soome {
358522028508SToomas Soome 	int rc = 0;
358622028508SToomas Soome 
358722028508SToomas Soome 	if (dn->dn_bonustype == DMU_OT_SA) {
358822028508SToomas Soome 		sa_hdr_phys_t *sahdrp = NULL;
358922028508SToomas Soome 		size_t size = 0;
359022028508SToomas Soome 		void *buf = NULL;
359122028508SToomas Soome 		int hdrsize;
359222028508SToomas Soome 		char *p;
359322028508SToomas Soome 
359422028508SToomas Soome 		if (dn->dn_bonuslen != 0) {
359522028508SToomas Soome 			sahdrp = (sa_hdr_phys_t *)DN_BONUS(dn);
359622028508SToomas Soome 		} else {
359722028508SToomas Soome 			blkptr_t *bp;
359822028508SToomas Soome 
359922028508SToomas Soome 			if ((dn->dn_flags & DNODE_FLAG_SPILL_BLKPTR) == 0)
360022028508SToomas Soome 				return (EIO);
360122028508SToomas Soome 			bp = DN_SPILL_BLKPTR(dn);
360222028508SToomas Soome 
360322028508SToomas Soome 			size = BP_GET_LSIZE(bp);
360422028508SToomas Soome 			buf = malloc(size);
360522028508SToomas Soome 			if (buf == NULL)
360622028508SToomas Soome 				rc = ENOMEM;
360722028508SToomas Soome 			else
360822028508SToomas Soome 				rc = zio_read(spa, bp, buf);
360922028508SToomas Soome 			if (rc != 0) {
361022028508SToomas Soome 				free(buf);
361122028508SToomas Soome 				return (rc);
361222028508SToomas Soome 			}
361322028508SToomas Soome 			sahdrp = buf;
361422028508SToomas Soome 		}
361522028508SToomas Soome 		hdrsize = SA_HDR_SIZE(sahdrp);
361622028508SToomas Soome 		p = (char *)((uintptr_t)sahdrp + hdrsize + SA_SYMLINK_OFFSET);
361722028508SToomas Soome 		memcpy(path, p, psize);
361822028508SToomas Soome 		free(buf);
361922028508SToomas Soome 		return (0);
362022028508SToomas Soome 	}
362122028508SToomas Soome 	/*
362222028508SToomas Soome 	 * Second test is purely to silence bogus compiler
362322028508SToomas Soome 	 * warning about accessing past the end of dn_bonus.
362422028508SToomas Soome 	 */
362522028508SToomas Soome 	if (psize + sizeof (znode_phys_t) <= dn->dn_bonuslen &&
362622028508SToomas Soome 	    sizeof (znode_phys_t) <= sizeof (dn->dn_bonus)) {
362722028508SToomas Soome 		memcpy(path, &dn->dn_bonus[sizeof (znode_phys_t)], psize);
362822028508SToomas Soome 	} else {
362922028508SToomas Soome 		rc = dnode_read(spa, dn, 0, path, psize);
363022028508SToomas Soome 	}
363122028508SToomas Soome 	return (rc);
363222028508SToomas Soome }
363322028508SToomas Soome 
363422028508SToomas Soome struct obj_list {
363522028508SToomas Soome 	uint64_t		objnum;
363622028508SToomas Soome 	STAILQ_ENTRY(obj_list)	entry;
363722028508SToomas Soome };
363822028508SToomas Soome 
363922028508SToomas Soome /*
364022028508SToomas Soome  * Lookup a file and return its dnode.
364122028508SToomas Soome  */
364222028508SToomas Soome static int
zfs_lookup(const struct zfsmount * mnt,const char * upath,dnode_phys_t * dnode)364322028508SToomas Soome zfs_lookup(const struct zfsmount *mnt, const char *upath, dnode_phys_t *dnode)
364422028508SToomas Soome {
364522028508SToomas Soome 	int rc;
364622028508SToomas Soome 	uint64_t objnum;
364722028508SToomas Soome 	const spa_t *spa;
364822028508SToomas Soome 	dnode_phys_t dn;
364922028508SToomas Soome 	const char *p, *q;
365022028508SToomas Soome 	char element[256];
365122028508SToomas Soome 	char path[1024];
365222028508SToomas Soome 	int symlinks_followed = 0;
365322028508SToomas Soome 	struct stat sb;
365422028508SToomas Soome 	struct obj_list *entry, *tentry;
365522028508SToomas Soome 	STAILQ_HEAD(, obj_list) on_cache = STAILQ_HEAD_INITIALIZER(on_cache);
365622028508SToomas Soome 
365722028508SToomas Soome 	spa = mnt->spa;
365822028508SToomas Soome 	if (mnt->objset.os_type != DMU_OST_ZFS) {
365922028508SToomas Soome 		printf("ZFS: unexpected object set type %ju\n",
366022028508SToomas Soome 		    (uintmax_t)mnt->objset.os_type);
366122028508SToomas Soome 		return (EIO);
366222028508SToomas Soome 	}
366322028508SToomas Soome 
366422028508SToomas Soome 	if ((entry = malloc(sizeof (struct obj_list))) == NULL)
366522028508SToomas Soome 		return (ENOMEM);
366622028508SToomas Soome 
366722028508SToomas Soome 	/*
366822028508SToomas Soome 	 * Get the root directory dnode.
366922028508SToomas Soome 	 */
367022028508SToomas Soome 	rc = objset_get_dnode(spa, &mnt->objset, MASTER_NODE_OBJ, &dn);
367122028508SToomas Soome 	if (rc) {
367222028508SToomas Soome 		free(entry);
367322028508SToomas Soome 		return (rc);
367422028508SToomas Soome 	}
367522028508SToomas Soome 
367622028508SToomas Soome 	rc = zap_lookup(spa, &dn, ZFS_ROOT_OBJ, sizeof (objnum), 1, &objnum);
367722028508SToomas Soome 	if (rc) {
367822028508SToomas Soome 		free(entry);
367922028508SToomas Soome 		return (rc);
368022028508SToomas Soome 	}
368122028508SToomas Soome 	entry->objnum = objnum;
368222028508SToomas Soome 	STAILQ_INSERT_HEAD(&on_cache, entry, entry);
368322028508SToomas Soome 
368422028508SToomas Soome 	rc = objset_get_dnode(spa, &mnt->objset, objnum, &dn);
368522028508SToomas Soome 	if (rc != 0)
368622028508SToomas Soome 		goto done;
368722028508SToomas Soome 
368822028508SToomas Soome 	p = upath;
368922028508SToomas Soome 	while (p && *p) {
369022028508SToomas Soome 		rc = objset_get_dnode(spa, &mnt->objset, objnum, &dn);
369122028508SToomas Soome 		if (rc != 0)
369222028508SToomas Soome 			goto done;
369322028508SToomas Soome 
369422028508SToomas Soome 		while (*p == '/')
369522028508SToomas Soome 			p++;
369622028508SToomas Soome 		if (*p == '\0')
369722028508SToomas Soome 			break;
369822028508SToomas Soome 		q = p;
369922028508SToomas Soome 		while (*q != '\0' && *q != '/')
370022028508SToomas Soome 			q++;
370122028508SToomas Soome 
370222028508SToomas Soome 		/* skip dot */
370322028508SToomas Soome 		if (p + 1 == q && p[0] == '.') {
370422028508SToomas Soome 			p++;
370522028508SToomas Soome 			continue;
370622028508SToomas Soome 		}
370722028508SToomas Soome 		/* double dot */
370822028508SToomas Soome 		if (p + 2 == q && p[0] == '.' && p[1] == '.') {
370922028508SToomas Soome 			p += 2;
371022028508SToomas Soome 			if (STAILQ_FIRST(&on_cache) ==
371122028508SToomas Soome 			    STAILQ_LAST(&on_cache, obj_list, entry)) {
371222028508SToomas Soome 				rc = ENOENT;
371322028508SToomas Soome 				goto done;
371422028508SToomas Soome 			}
371522028508SToomas Soome 			entry = STAILQ_FIRST(&on_cache);
371622028508SToomas Soome 			STAILQ_REMOVE_HEAD(&on_cache, entry);
371722028508SToomas Soome 			free(entry);
371822028508SToomas Soome 			objnum = (STAILQ_FIRST(&on_cache))->objnum;
371922028508SToomas Soome 			continue;
372022028508SToomas Soome 		}
372122028508SToomas Soome 		if (q - p + 1 > sizeof (element)) {
372222028508SToomas Soome 			rc = ENAMETOOLONG;
372322028508SToomas Soome 			goto done;
372422028508SToomas Soome 		}
372522028508SToomas Soome 		memcpy(element, p, q - p);
372622028508SToomas Soome 		element[q - p] = 0;
372722028508SToomas Soome 		p = q;
372822028508SToomas Soome 
372922028508SToomas Soome 		if ((rc = zfs_dnode_stat(spa, &dn, &sb)) != 0)
373022028508SToomas Soome 			goto done;
373122028508SToomas Soome 		if (!S_ISDIR(sb.st_mode)) {
373222028508SToomas Soome 			rc = ENOTDIR;
373322028508SToomas Soome 			goto done;
373422028508SToomas Soome 		}
373522028508SToomas Soome 
373622028508SToomas Soome 		rc = zap_lookup(spa, &dn, element, sizeof (objnum), 1, &objnum);
373722028508SToomas Soome 		if (rc)
373822028508SToomas Soome 			goto done;
373922028508SToomas Soome 		objnum = ZFS_DIRENT_OBJ(objnum);
374022028508SToomas Soome 
374122028508SToomas Soome 		if ((entry = malloc(sizeof (struct obj_list))) == NULL) {
374222028508SToomas Soome 			rc = ENOMEM;
374322028508SToomas Soome 			goto done;
374422028508SToomas Soome 		}
374522028508SToomas Soome 		entry->objnum = objnum;
374622028508SToomas Soome 		STAILQ_INSERT_HEAD(&on_cache, entry, entry);
374722028508SToomas Soome 		rc = objset_get_dnode(spa, &mnt->objset, objnum, &dn);
374822028508SToomas Soome 		if (rc)
374922028508SToomas Soome 			goto done;
375022028508SToomas Soome 
375122028508SToomas Soome 		/*
375222028508SToomas Soome 		 * Check for symlink.
375322028508SToomas Soome 		 */
375422028508SToomas Soome 		rc = zfs_dnode_stat(spa, &dn, &sb);
375522028508SToomas Soome 		if (rc)
375622028508SToomas Soome 			goto done;
375722028508SToomas Soome 		if (S_ISLNK(sb.st_mode)) {
375822028508SToomas Soome 			if (symlinks_followed > 10) {
375922028508SToomas Soome 				rc = EMLINK;
376022028508SToomas Soome 				goto done;
376122028508SToomas Soome 			}
376222028508SToomas Soome 			symlinks_followed++;
376322028508SToomas Soome 
376422028508SToomas Soome 			/*
376522028508SToomas Soome 			 * Read the link value and copy the tail of our
376622028508SToomas Soome 			 * current path onto the end.
376722028508SToomas Soome 			 */
376822028508SToomas Soome 			if (sb.st_size + strlen(p) + 1 > sizeof (path)) {
376922028508SToomas Soome 				rc = ENAMETOOLONG;
377022028508SToomas Soome 				goto done;
377122028508SToomas Soome 			}
377222028508SToomas Soome 			strcpy(&path[sb.st_size], p);
377322028508SToomas Soome 
377422028508SToomas Soome 			rc = zfs_dnode_readlink(spa, &dn, path, sb.st_size);
377522028508SToomas Soome 			if (rc != 0)
377622028508SToomas Soome 				goto done;
377722028508SToomas Soome 
377822028508SToomas Soome 			/*
377922028508SToomas Soome 			 * Restart with the new path, starting either at
378022028508SToomas Soome 			 * the root or at the parent depending whether or
378122028508SToomas Soome 			 * not the link is relative.
378222028508SToomas Soome 			 */
378322028508SToomas Soome 			p = path;
378422028508SToomas Soome 			if (*p == '/') {
378522028508SToomas Soome 				while (STAILQ_FIRST(&on_cache) !=
378622028508SToomas Soome 				    STAILQ_LAST(&on_cache, obj_list, entry)) {
378722028508SToomas Soome 					entry = STAILQ_FIRST(&on_cache);
378822028508SToomas Soome 					STAILQ_REMOVE_HEAD(&on_cache, entry);
378922028508SToomas Soome 					free(entry);
379022028508SToomas Soome 				}
379122028508SToomas Soome 			} else {
379222028508SToomas Soome 				entry = STAILQ_FIRST(&on_cache);
379322028508SToomas Soome 				STAILQ_REMOVE_HEAD(&on_cache, entry);
379422028508SToomas Soome 				free(entry);
379522028508SToomas Soome 			}
379622028508SToomas Soome 			objnum = (STAILQ_FIRST(&on_cache))->objnum;
379722028508SToomas Soome 		}
379822028508SToomas Soome 	}
379922028508SToomas Soome 
380022028508SToomas Soome 	*dnode = dn;
380122028508SToomas Soome done:
380222028508SToomas Soome 	STAILQ_FOREACH_SAFE(entry, &on_cache, entry, tentry)
380322028508SToomas Soome 		free(entry);
380422028508SToomas Soome 	return (rc);
380522028508SToomas Soome }
3806