1168404Spjd/*
2168404Spjd * CDDL HEADER START
3168404Spjd *
4168404Spjd * The contents of this file are subject to the terms of the
5168404Spjd * Common Development and Distribution License (the "License").
6168404Spjd * You may not use this file except in compliance with the License.
7168404Spjd *
8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9168404Spjd * or http://www.opensolaris.org/os/licensing.
10168404Spjd * See the License for the specific language governing permissions
11168404Spjd * and limitations under the License.
12168404Spjd *
13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each
14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15168404Spjd * If applicable, add the following below this CDDL HEADER, with the
16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying
17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner]
18168404Spjd *
19168404Spjd * CDDL HEADER END
20168404Spjd */
21168404Spjd
22168404Spjd/*
23219089Spjd * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
24168404Spjd */
25168404Spjd
26168404Spjd#include <sys/debug.h>
27168404Spjd#include <sys/nvpair.h>
28168404Spjd#include <sys/nvpair_impl.h>
29168404Spjd#include <rpc/types.h>
30168404Spjd#include <rpc/xdr.h>
31168404Spjd
32168404Spjd#if defined(_KERNEL) && !defined(_BOOT)
33168404Spjd#include <sys/varargs.h>
34185029Spjd#include <sys/sunddi.h>
35168404Spjd#else
36168404Spjd#include <stdarg.h>
37185029Spjd#include <stdlib.h>
38185029Spjd#include <string.h>
39219089Spjd#include <strings.h>
40168404Spjd#endif
41168404Spjd
42168404Spjd#ifndef	offsetof
43185029Spjd#define	offsetof(s, m)		((size_t)(&(((s *)0)->m)))
44168404Spjd#endif
45185029Spjd#define	skip_whitespace(p)	while ((*(p) == ' ') || (*(p) == '\t')) p++
46168404Spjd
47168404Spjd/*
48168404Spjd * nvpair.c - Provides kernel & userland interfaces for manipulating
49168404Spjd *	name-value pairs.
50168404Spjd *
51168404Spjd * Overview Diagram
52168404Spjd *
53168404Spjd *  +--------------+
54168404Spjd *  |  nvlist_t    |
55168404Spjd *  |--------------|
56168404Spjd *  | nvl_version  |
57168404Spjd *  | nvl_nvflag   |
58168404Spjd *  | nvl_priv    -+-+
59168404Spjd *  | nvl_flag     | |
60168404Spjd *  | nvl_pad      | |
61168404Spjd *  +--------------+ |
62168404Spjd *                   V
63168404Spjd *      +--------------+      last i_nvp in list
64168404Spjd *      | nvpriv_t     |  +--------------------->
65168404Spjd *      |--------------|  |
66168404Spjd *   +--+- nvp_list    |  |   +------------+
67168404Spjd *   |  |  nvp_last   -+--+   + nv_alloc_t |
68168404Spjd *   |  |  nvp_curr    |      |------------|
69168404Spjd *   |  |  nvp_nva    -+----> | nva_ops    |
70168404Spjd *   |  |  nvp_stat    |      | nva_arg    |
71168404Spjd *   |  +--------------+      +------------+
72168404Spjd *   |
73168404Spjd *   +-------+
74168404Spjd *           V
75168404Spjd *   +---------------------+      +-------------------+
76168404Spjd *   |  i_nvp_t            |  +-->|  i_nvp_t          |  +-->
77168404Spjd *   |---------------------|  |   |-------------------|  |
78168404Spjd *   | nvi_next           -+--+   | nvi_next         -+--+
79168404Spjd *   | nvi_prev (NULL)     | <----+ nvi_prev          |
80168404Spjd *   | . . . . . . . . . . |      | . . . . . . . . . |
81168404Spjd *   | nvp (nvpair_t)      |      | nvp (nvpair_t)    |
82168404Spjd *   |  - nvp_size         |      |  - nvp_size       |
83168404Spjd *   |  - nvp_name_sz      |      |  - nvp_name_sz    |
84168404Spjd *   |  - nvp_value_elem   |      |  - nvp_value_elem |
85168404Spjd *   |  - nvp_type         |      |  - nvp_type       |
86168404Spjd *   |  - data ...         |      |  - data ...       |
87168404Spjd *   +---------------------+      +-------------------+
88168404Spjd *
89168404Spjd *
90168404Spjd *
91168404Spjd *   +---------------------+              +---------------------+
92168404Spjd *   |  i_nvp_t            |  +-->    +-->|  i_nvp_t (last)     |
93168404Spjd *   |---------------------|  |       |   |---------------------|
94168404Spjd *   |  nvi_next          -+--+ ... --+   | nvi_next (NULL)     |
95168404Spjd * <-+- nvi_prev           |<-- ...  <----+ nvi_prev            |
96168404Spjd *   | . . . . . . . . .   |              | . . . . . . . . .   |
97168404Spjd *   | nvp (nvpair_t)      |              | nvp (nvpair_t)      |
98168404Spjd *   |  - nvp_size         |              |  - nvp_size         |
99168404Spjd *   |  - nvp_name_sz      |              |  - nvp_name_sz      |
100168404Spjd *   |  - nvp_value_elem   |              |  - nvp_value_elem   |
101168404Spjd *   |  - DATA_TYPE_NVLIST |              |  - nvp_type         |
102168404Spjd *   |  - data (embedded)  |              |  - data ...         |
103168404Spjd *   |    nvlist name      |              +---------------------+
104168404Spjd *   |  +--------------+   |
105168404Spjd *   |  |  nvlist_t    |   |
106168404Spjd *   |  |--------------|   |
107168404Spjd *   |  | nvl_version  |   |
108168404Spjd *   |  | nvl_nvflag   |   |
109168404Spjd *   |  | nvl_priv   --+---+---->
110168404Spjd *   |  | nvl_flag     |   |
111168404Spjd *   |  | nvl_pad      |   |
112168404Spjd *   |  +--------------+   |
113168404Spjd *   +---------------------+
114168404Spjd *
115168404Spjd *
116168404Spjd * N.B. nvpair_t may be aligned on 4 byte boundary, so +4 will
117168404Spjd * allow value to be aligned on 8 byte boundary
118168404Spjd *
119168404Spjd * name_len is the length of the name string including the null terminator
120168404Spjd * so it must be >= 1
121168404Spjd */
122168404Spjd#define	NVP_SIZE_CALC(name_len, data_len) \
123168404Spjd	(NV_ALIGN((sizeof (nvpair_t)) + name_len) + NV_ALIGN(data_len))
124168404Spjd
125168404Spjdstatic int i_get_value_size(data_type_t type, const void *data, uint_t nelem);
126168404Spjdstatic int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type,
127168404Spjd    uint_t nelem, const void *data);
128168404Spjd
129168404Spjd#define	NV_STAT_EMBEDDED	0x1
130168404Spjd#define	EMBEDDED_NVL(nvp)	((nvlist_t *)(void *)NVP_VALUE(nvp))
131168404Spjd#define	EMBEDDED_NVL_ARRAY(nvp)	((nvlist_t **)(void *)NVP_VALUE(nvp))
132168404Spjd
133168404Spjd#define	NVP_VALOFF(nvp)	(NV_ALIGN(sizeof (nvpair_t) + (nvp)->nvp_name_sz))
134168404Spjd#define	NVPAIR2I_NVP(nvp) \
135168404Spjd	((i_nvp_t *)((size_t)(nvp) - offsetof(i_nvp_t, nvi_nvp)))
136168404Spjd
137168404Spjd
138168404Spjdint
139168404Spjdnv_alloc_init(nv_alloc_t *nva, const nv_alloc_ops_t *nvo, /* args */ ...)
140168404Spjd{
141168404Spjd	va_list valist;
142168404Spjd	int err = 0;
143168404Spjd
144168404Spjd	nva->nva_ops = nvo;
145168404Spjd	nva->nva_arg = NULL;
146168404Spjd
147168404Spjd	va_start(valist, nvo);
148168404Spjd	if (nva->nva_ops->nv_ao_init != NULL)
149168404Spjd		err = nva->nva_ops->nv_ao_init(nva, valist);
150168404Spjd	va_end(valist);
151168404Spjd
152168404Spjd	return (err);
153168404Spjd}
154168404Spjd
155168404Spjdvoid
156168404Spjdnv_alloc_reset(nv_alloc_t *nva)
157168404Spjd{
158168404Spjd	if (nva->nva_ops->nv_ao_reset != NULL)
159168404Spjd		nva->nva_ops->nv_ao_reset(nva);
160168404Spjd}
161168404Spjd
162168404Spjdvoid
163168404Spjdnv_alloc_fini(nv_alloc_t *nva)
164168404Spjd{
165168404Spjd	if (nva->nva_ops->nv_ao_fini != NULL)
166168404Spjd		nva->nva_ops->nv_ao_fini(nva);
167168404Spjd}
168168404Spjd
169168404Spjdnv_alloc_t *
170168404Spjdnvlist_lookup_nv_alloc(nvlist_t *nvl)
171168404Spjd{
172168404Spjd	nvpriv_t *priv;
173168404Spjd
174168404Spjd	if (nvl == NULL ||
175168404Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
176168404Spjd		return (NULL);
177168404Spjd
178168404Spjd	return (priv->nvp_nva);
179168404Spjd}
180168404Spjd
181168404Spjdstatic void *
182168404Spjdnv_mem_zalloc(nvpriv_t *nvp, size_t size)
183168404Spjd{
184168404Spjd	nv_alloc_t *nva = nvp->nvp_nva;
185168404Spjd	void *buf;
186168404Spjd
187168404Spjd	if ((buf = nva->nva_ops->nv_ao_alloc(nva, size)) != NULL)
188168404Spjd		bzero(buf, size);
189168404Spjd
190168404Spjd	return (buf);
191168404Spjd}
192168404Spjd
193168404Spjdstatic void
194168404Spjdnv_mem_free(nvpriv_t *nvp, void *buf, size_t size)
195168404Spjd{
196168404Spjd	nv_alloc_t *nva = nvp->nvp_nva;
197168404Spjd
198168404Spjd	nva->nva_ops->nv_ao_free(nva, buf, size);
199168404Spjd}
200168404Spjd
201168404Spjdstatic void
202168404Spjdnv_priv_init(nvpriv_t *priv, nv_alloc_t *nva, uint32_t stat)
203168404Spjd{
204185029Spjd	bzero(priv, sizeof (nvpriv_t));
205168404Spjd
206168404Spjd	priv->nvp_nva = nva;
207168404Spjd	priv->nvp_stat = stat;
208168404Spjd}
209168404Spjd
210168404Spjdstatic nvpriv_t *
211168404Spjdnv_priv_alloc(nv_alloc_t *nva)
212168404Spjd{
213168404Spjd	nvpriv_t *priv;
214168404Spjd
215168404Spjd	/*
216168404Spjd	 * nv_mem_alloc() cannot called here because it needs the priv
217168404Spjd	 * argument.
218168404Spjd	 */
219168404Spjd	if ((priv = nva->nva_ops->nv_ao_alloc(nva, sizeof (nvpriv_t))) == NULL)
220168404Spjd		return (NULL);
221168404Spjd
222168404Spjd	nv_priv_init(priv, nva, 0);
223168404Spjd
224168404Spjd	return (priv);
225168404Spjd}
226168404Spjd
227168404Spjd/*
228168404Spjd * Embedded lists need their own nvpriv_t's.  We create a new
229168404Spjd * nvpriv_t using the parameters and allocator from the parent
230168404Spjd * list's nvpriv_t.
231168404Spjd */
232168404Spjdstatic nvpriv_t *
233168404Spjdnv_priv_alloc_embedded(nvpriv_t *priv)
234168404Spjd{
235168404Spjd	nvpriv_t *emb_priv;
236168404Spjd
237168404Spjd	if ((emb_priv = nv_mem_zalloc(priv, sizeof (nvpriv_t))) == NULL)
238168404Spjd		return (NULL);
239168404Spjd
240168404Spjd	nv_priv_init(emb_priv, priv->nvp_nva, NV_STAT_EMBEDDED);
241168404Spjd
242168404Spjd	return (emb_priv);
243168404Spjd}
244168404Spjd
245168404Spjdstatic void
246168404Spjdnvlist_init(nvlist_t *nvl, uint32_t nvflag, nvpriv_t *priv)
247168404Spjd{
248168404Spjd	nvl->nvl_version = NV_VERSION;
249168404Spjd	nvl->nvl_nvflag = nvflag & (NV_UNIQUE_NAME|NV_UNIQUE_NAME_TYPE);
250168404Spjd	nvl->nvl_priv = (uint64_t)(uintptr_t)priv;
251168404Spjd	nvl->nvl_flag = 0;
252168404Spjd	nvl->nvl_pad = 0;
253168404Spjd}
254168404Spjd
255219089Spjduint_t
256219089Spjdnvlist_nvflag(nvlist_t *nvl)
257219089Spjd{
258219089Spjd	return (nvl->nvl_nvflag);
259219089Spjd}
260219089Spjd
261168404Spjd/*
262168404Spjd * nvlist_alloc - Allocate nvlist.
263168404Spjd */
264168404Spjd/*ARGSUSED1*/
265168404Spjdint
266168404Spjdnvlist_alloc(nvlist_t **nvlp, uint_t nvflag, int kmflag)
267168404Spjd{
268168404Spjd#if defined(_KERNEL) && !defined(_BOOT)
269168404Spjd	return (nvlist_xalloc(nvlp, nvflag,
270168404Spjd	    (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep)));
271168404Spjd#else
272168404Spjd	return (nvlist_xalloc(nvlp, nvflag, nv_alloc_nosleep));
273168404Spjd#endif
274168404Spjd}
275168404Spjd
276168404Spjdint
277168404Spjdnvlist_xalloc(nvlist_t **nvlp, uint_t nvflag, nv_alloc_t *nva)
278168404Spjd{
279168404Spjd	nvpriv_t *priv;
280168404Spjd
281168404Spjd	if (nvlp == NULL || nva == NULL)
282168404Spjd		return (EINVAL);
283168404Spjd
284168404Spjd	if ((priv = nv_priv_alloc(nva)) == NULL)
285168404Spjd		return (ENOMEM);
286168404Spjd
287168404Spjd	if ((*nvlp = nv_mem_zalloc(priv,
288168404Spjd	    NV_ALIGN(sizeof (nvlist_t)))) == NULL) {
289168404Spjd		nv_mem_free(priv, priv, sizeof (nvpriv_t));
290168404Spjd		return (ENOMEM);
291168404Spjd	}
292168404Spjd
293168404Spjd	nvlist_init(*nvlp, nvflag, priv);
294168404Spjd
295168404Spjd	return (0);
296168404Spjd}
297168404Spjd
298168404Spjd/*
299168404Spjd * nvp_buf_alloc - Allocate i_nvp_t for storing a new nv pair.
300168404Spjd */
301168404Spjdstatic nvpair_t *
302168404Spjdnvp_buf_alloc(nvlist_t *nvl, size_t len)
303168404Spjd{
304168404Spjd	nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv;
305168404Spjd	i_nvp_t *buf;
306168404Spjd	nvpair_t *nvp;
307168404Spjd	size_t nvsize;
308168404Spjd
309168404Spjd	/*
310168404Spjd	 * Allocate the buffer
311168404Spjd	 */
312168404Spjd	nvsize = len + offsetof(i_nvp_t, nvi_nvp);
313168404Spjd
314168404Spjd	if ((buf = nv_mem_zalloc(priv, nvsize)) == NULL)
315168404Spjd		return (NULL);
316168404Spjd
317168404Spjd	nvp = &buf->nvi_nvp;
318168404Spjd	nvp->nvp_size = len;
319168404Spjd
320168404Spjd	return (nvp);
321168404Spjd}
322168404Spjd
323168404Spjd/*
324168404Spjd * nvp_buf_free - de-Allocate an i_nvp_t.
325168404Spjd */
326168404Spjdstatic void
327168404Spjdnvp_buf_free(nvlist_t *nvl, nvpair_t *nvp)
328168404Spjd{
329168404Spjd	nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv;
330168404Spjd	size_t nvsize = nvp->nvp_size + offsetof(i_nvp_t, nvi_nvp);
331168404Spjd
332168404Spjd	nv_mem_free(priv, NVPAIR2I_NVP(nvp), nvsize);
333168404Spjd}
334168404Spjd
335168404Spjd/*
336168404Spjd * nvp_buf_link - link a new nv pair into the nvlist.
337168404Spjd */
338168404Spjdstatic void
339168404Spjdnvp_buf_link(nvlist_t *nvl, nvpair_t *nvp)
340168404Spjd{
341168404Spjd	nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv;
342168404Spjd	i_nvp_t *curr = NVPAIR2I_NVP(nvp);
343168404Spjd
344168404Spjd	/* Put element at end of nvlist */
345168404Spjd	if (priv->nvp_list == NULL) {
346168404Spjd		priv->nvp_list = priv->nvp_last = curr;
347168404Spjd	} else {
348168404Spjd		curr->nvi_prev = priv->nvp_last;
349168404Spjd		priv->nvp_last->nvi_next = curr;
350168404Spjd		priv->nvp_last = curr;
351168404Spjd	}
352168404Spjd}
353168404Spjd
354168404Spjd/*
355168404Spjd * nvp_buf_unlink - unlink an removed nvpair out of the nvlist.
356168404Spjd */
357168404Spjdstatic void
358168404Spjdnvp_buf_unlink(nvlist_t *nvl, nvpair_t *nvp)
359168404Spjd{
360168404Spjd	nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv;
361168404Spjd	i_nvp_t *curr = NVPAIR2I_NVP(nvp);
362168404Spjd
363168404Spjd	/*
364168404Spjd	 * protect nvlist_next_nvpair() against walking on freed memory.
365168404Spjd	 */
366168404Spjd	if (priv->nvp_curr == curr)
367168404Spjd		priv->nvp_curr = curr->nvi_next;
368168404Spjd
369168404Spjd	if (curr == priv->nvp_list)
370168404Spjd		priv->nvp_list = curr->nvi_next;
371168404Spjd	else
372168404Spjd		curr->nvi_prev->nvi_next = curr->nvi_next;
373168404Spjd
374168404Spjd	if (curr == priv->nvp_last)
375168404Spjd		priv->nvp_last = curr->nvi_prev;
376168404Spjd	else
377168404Spjd		curr->nvi_next->nvi_prev = curr->nvi_prev;
378168404Spjd}
379168404Spjd
380168404Spjd/*
381168404Spjd * take a nvpair type and number of elements and make sure the are valid
382168404Spjd */
383168404Spjdstatic int
384168404Spjdi_validate_type_nelem(data_type_t type, uint_t nelem)
385168404Spjd{
386168404Spjd	switch (type) {
387168404Spjd	case DATA_TYPE_BOOLEAN:
388168404Spjd		if (nelem != 0)
389168404Spjd			return (EINVAL);
390168404Spjd		break;
391168404Spjd	case DATA_TYPE_BOOLEAN_VALUE:
392168404Spjd	case DATA_TYPE_BYTE:
393168404Spjd	case DATA_TYPE_INT8:
394168404Spjd	case DATA_TYPE_UINT8:
395168404Spjd	case DATA_TYPE_INT16:
396168404Spjd	case DATA_TYPE_UINT16:
397168404Spjd	case DATA_TYPE_INT32:
398168404Spjd	case DATA_TYPE_UINT32:
399168404Spjd	case DATA_TYPE_INT64:
400168404Spjd	case DATA_TYPE_UINT64:
401168404Spjd	case DATA_TYPE_STRING:
402168404Spjd	case DATA_TYPE_HRTIME:
403168404Spjd	case DATA_TYPE_NVLIST:
404185029Spjd#if !defined(_KERNEL)
405185029Spjd	case DATA_TYPE_DOUBLE:
406185029Spjd#endif
407168404Spjd		if (nelem != 1)
408168404Spjd			return (EINVAL);
409168404Spjd		break;
410168404Spjd	case DATA_TYPE_BOOLEAN_ARRAY:
411168404Spjd	case DATA_TYPE_BYTE_ARRAY:
412168404Spjd	case DATA_TYPE_INT8_ARRAY:
413168404Spjd	case DATA_TYPE_UINT8_ARRAY:
414168404Spjd	case DATA_TYPE_INT16_ARRAY:
415168404Spjd	case DATA_TYPE_UINT16_ARRAY:
416168404Spjd	case DATA_TYPE_INT32_ARRAY:
417168404Spjd	case DATA_TYPE_UINT32_ARRAY:
418168404Spjd	case DATA_TYPE_INT64_ARRAY:
419168404Spjd	case DATA_TYPE_UINT64_ARRAY:
420168404Spjd	case DATA_TYPE_STRING_ARRAY:
421168404Spjd	case DATA_TYPE_NVLIST_ARRAY:
422168404Spjd		/* we allow arrays with 0 elements */
423168404Spjd		break;
424168404Spjd	default:
425168404Spjd		return (EINVAL);
426168404Spjd	}
427168404Spjd	return (0);
428168404Spjd}
429168404Spjd
430168404Spjd/*
431168404Spjd * Verify nvp_name_sz and check the name string length.
432168404Spjd */
433168404Spjdstatic int
434168404Spjdi_validate_nvpair_name(nvpair_t *nvp)
435168404Spjd{
436168404Spjd	if ((nvp->nvp_name_sz <= 0) ||
437168404Spjd	    (nvp->nvp_size < NVP_SIZE_CALC(nvp->nvp_name_sz, 0)))
438168404Spjd		return (EFAULT);
439168404Spjd
440168404Spjd	/* verify the name string, make sure its terminated */
441168404Spjd	if (NVP_NAME(nvp)[nvp->nvp_name_sz - 1] != '\0')
442168404Spjd		return (EFAULT);
443168404Spjd
444168404Spjd	return (strlen(NVP_NAME(nvp)) == nvp->nvp_name_sz - 1 ? 0 : EFAULT);
445168404Spjd}
446168404Spjd
447168404Spjdstatic int
448168404Spjdi_validate_nvpair_value(data_type_t type, uint_t nelem, const void *data)
449168404Spjd{
450168404Spjd	switch (type) {
451168404Spjd	case DATA_TYPE_BOOLEAN_VALUE:
452168404Spjd		if (*(boolean_t *)data != B_TRUE &&
453168404Spjd		    *(boolean_t *)data != B_FALSE)
454168404Spjd			return (EINVAL);
455168404Spjd		break;
456168404Spjd	case DATA_TYPE_BOOLEAN_ARRAY: {
457168404Spjd		int i;
458168404Spjd
459168404Spjd		for (i = 0; i < nelem; i++)
460168404Spjd			if (((boolean_t *)data)[i] != B_TRUE &&
461168404Spjd			    ((boolean_t *)data)[i] != B_FALSE)
462168404Spjd				return (EINVAL);
463168404Spjd		break;
464168404Spjd	}
465168404Spjd	default:
466168404Spjd		break;
467168404Spjd	}
468168404Spjd
469168404Spjd	return (0);
470168404Spjd}
471168404Spjd
472168404Spjd/*
473168404Spjd * This function takes a pointer to what should be a nvpair and it's size
474168404Spjd * and then verifies that all the nvpair fields make sense and can be
475168404Spjd * trusted.  This function is used when decoding packed nvpairs.
476168404Spjd */
477168404Spjdstatic int
478168404Spjdi_validate_nvpair(nvpair_t *nvp)
479168404Spjd{
480168404Spjd	data_type_t type = NVP_TYPE(nvp);
481168404Spjd	int size1, size2;
482168404Spjd
483168404Spjd	/* verify nvp_name_sz, check the name string length */
484168404Spjd	if (i_validate_nvpair_name(nvp) != 0)
485168404Spjd		return (EFAULT);
486168404Spjd
487168404Spjd	if (i_validate_nvpair_value(type, NVP_NELEM(nvp), NVP_VALUE(nvp)) != 0)
488168404Spjd		return (EFAULT);
489168404Spjd
490168404Spjd	/*
491168404Spjd	 * verify nvp_type, nvp_value_elem, and also possibly
492168404Spjd	 * verify string values and get the value size.
493168404Spjd	 */
494168404Spjd	size2 = i_get_value_size(type, NVP_VALUE(nvp), NVP_NELEM(nvp));
495168404Spjd	size1 = nvp->nvp_size - NVP_VALOFF(nvp);
496168404Spjd	if (size2 < 0 || size1 != NV_ALIGN(size2))
497168404Spjd		return (EFAULT);
498168404Spjd
499168404Spjd	return (0);
500168404Spjd}
501168404Spjd
502168404Spjdstatic int
503168404Spjdnvlist_copy_pairs(nvlist_t *snvl, nvlist_t *dnvl)
504168404Spjd{
505168404Spjd	nvpriv_t *priv;
506168404Spjd	i_nvp_t *curr;
507168404Spjd
508168404Spjd	if ((priv = (nvpriv_t *)(uintptr_t)snvl->nvl_priv) == NULL)
509168404Spjd		return (EINVAL);
510168404Spjd
511168404Spjd	for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
512168404Spjd		nvpair_t *nvp = &curr->nvi_nvp;
513168404Spjd		int err;
514168404Spjd
515168404Spjd		if ((err = nvlist_add_common(dnvl, NVP_NAME(nvp), NVP_TYPE(nvp),
516168404Spjd		    NVP_NELEM(nvp), NVP_VALUE(nvp))) != 0)
517168404Spjd			return (err);
518168404Spjd	}
519168404Spjd
520168404Spjd	return (0);
521168404Spjd}
522168404Spjd
523168404Spjd/*
524168404Spjd * Frees all memory allocated for an nvpair (like embedded lists) with
525168404Spjd * the exception of the nvpair buffer itself.
526168404Spjd */
527168404Spjdstatic void
528168404Spjdnvpair_free(nvpair_t *nvp)
529168404Spjd{
530168404Spjd	switch (NVP_TYPE(nvp)) {
531168404Spjd	case DATA_TYPE_NVLIST:
532168404Spjd		nvlist_free(EMBEDDED_NVL(nvp));
533168404Spjd		break;
534168404Spjd	case DATA_TYPE_NVLIST_ARRAY: {
535168404Spjd		nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp);
536168404Spjd		int i;
537168404Spjd
538168404Spjd		for (i = 0; i < NVP_NELEM(nvp); i++)
539168404Spjd			if (nvlp[i] != NULL)
540168404Spjd				nvlist_free(nvlp[i]);
541168404Spjd		break;
542168404Spjd	}
543168404Spjd	default:
544168404Spjd		break;
545168404Spjd	}
546168404Spjd}
547168404Spjd
548168404Spjd/*
549168404Spjd * nvlist_free - free an unpacked nvlist
550168404Spjd */
551168404Spjdvoid
552168404Spjdnvlist_free(nvlist_t *nvl)
553168404Spjd{
554168404Spjd	nvpriv_t *priv;
555168404Spjd	i_nvp_t *curr;
556168404Spjd
557168404Spjd	if (nvl == NULL ||
558168404Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
559168404Spjd		return;
560168404Spjd
561168404Spjd	/*
562168404Spjd	 * Unpacked nvlist are linked through i_nvp_t
563168404Spjd	 */
564168404Spjd	curr = priv->nvp_list;
565168404Spjd	while (curr != NULL) {
566168404Spjd		nvpair_t *nvp = &curr->nvi_nvp;
567168404Spjd		curr = curr->nvi_next;
568168404Spjd
569168404Spjd		nvpair_free(nvp);
570168404Spjd		nvp_buf_free(nvl, nvp);
571168404Spjd	}
572168404Spjd
573168404Spjd	if (!(priv->nvp_stat & NV_STAT_EMBEDDED))
574168404Spjd		nv_mem_free(priv, nvl, NV_ALIGN(sizeof (nvlist_t)));
575168404Spjd	else
576168404Spjd		nvl->nvl_priv = 0;
577168404Spjd
578168404Spjd	nv_mem_free(priv, priv, sizeof (nvpriv_t));
579168404Spjd}
580168404Spjd
581168404Spjdstatic int
582168404Spjdnvlist_contains_nvp(nvlist_t *nvl, nvpair_t *nvp)
583168404Spjd{
584168404Spjd	nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv;
585168404Spjd	i_nvp_t *curr;
586168404Spjd
587168404Spjd	if (nvp == NULL)
588168404Spjd		return (0);
589168404Spjd
590168404Spjd	for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next)
591168404Spjd		if (&curr->nvi_nvp == nvp)
592168404Spjd			return (1);
593168404Spjd
594168404Spjd	return (0);
595168404Spjd}
596168404Spjd
597168404Spjd/*
598168404Spjd * Make a copy of nvlist
599168404Spjd */
600168404Spjd/*ARGSUSED1*/
601168404Spjdint
602168404Spjdnvlist_dup(nvlist_t *nvl, nvlist_t **nvlp, int kmflag)
603168404Spjd{
604168404Spjd#if defined(_KERNEL) && !defined(_BOOT)
605168404Spjd	return (nvlist_xdup(nvl, nvlp,
606168404Spjd	    (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep)));
607168404Spjd#else
608168404Spjd	return (nvlist_xdup(nvl, nvlp, nv_alloc_nosleep));
609168404Spjd#endif
610168404Spjd}
611168404Spjd
612168404Spjdint
613168404Spjdnvlist_xdup(nvlist_t *nvl, nvlist_t **nvlp, nv_alloc_t *nva)
614168404Spjd{
615168404Spjd	int err;
616168404Spjd	nvlist_t *ret;
617168404Spjd
618168404Spjd	if (nvl == NULL || nvlp == NULL)
619168404Spjd		return (EINVAL);
620168404Spjd
621168404Spjd	if ((err = nvlist_xalloc(&ret, nvl->nvl_nvflag, nva)) != 0)
622168404Spjd		return (err);
623168404Spjd
624168404Spjd	if ((err = nvlist_copy_pairs(nvl, ret)) != 0)
625168404Spjd		nvlist_free(ret);
626168404Spjd	else
627168404Spjd		*nvlp = ret;
628168404Spjd
629168404Spjd	return (err);
630168404Spjd}
631168404Spjd
632168404Spjd/*
633168404Spjd * Remove all with matching name
634168404Spjd */
635168404Spjdint
636168404Spjdnvlist_remove_all(nvlist_t *nvl, const char *name)
637168404Spjd{
638168404Spjd	nvpriv_t *priv;
639168404Spjd	i_nvp_t *curr;
640168404Spjd	int error = ENOENT;
641168404Spjd
642168404Spjd	if (nvl == NULL || name == NULL ||
643168404Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
644168404Spjd		return (EINVAL);
645168404Spjd
646168404Spjd	curr = priv->nvp_list;
647168404Spjd	while (curr != NULL) {
648168404Spjd		nvpair_t *nvp = &curr->nvi_nvp;
649168404Spjd
650168404Spjd		curr = curr->nvi_next;
651168404Spjd		if (strcmp(name, NVP_NAME(nvp)) != 0)
652168404Spjd			continue;
653168404Spjd
654168404Spjd		nvp_buf_unlink(nvl, nvp);
655168404Spjd		nvpair_free(nvp);
656168404Spjd		nvp_buf_free(nvl, nvp);
657168404Spjd
658168404Spjd		error = 0;
659168404Spjd	}
660168404Spjd
661168404Spjd	return (error);
662168404Spjd}
663168404Spjd
664168404Spjd/*
665168404Spjd * Remove first one with matching name and type
666168404Spjd */
667168404Spjdint
668168404Spjdnvlist_remove(nvlist_t *nvl, const char *name, data_type_t type)
669168404Spjd{
670168404Spjd	nvpriv_t *priv;
671168404Spjd	i_nvp_t *curr;
672168404Spjd
673168404Spjd	if (nvl == NULL || name == NULL ||
674168404Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
675168404Spjd		return (EINVAL);
676168404Spjd
677168404Spjd	curr = priv->nvp_list;
678168404Spjd	while (curr != NULL) {
679168404Spjd		nvpair_t *nvp = &curr->nvi_nvp;
680168404Spjd
681168404Spjd		if (strcmp(name, NVP_NAME(nvp)) == 0 && NVP_TYPE(nvp) == type) {
682168404Spjd			nvp_buf_unlink(nvl, nvp);
683168404Spjd			nvpair_free(nvp);
684168404Spjd			nvp_buf_free(nvl, nvp);
685168404Spjd
686168404Spjd			return (0);
687168404Spjd		}
688168404Spjd		curr = curr->nvi_next;
689168404Spjd	}
690168404Spjd
691168404Spjd	return (ENOENT);
692168404Spjd}
693168404Spjd
694219089Spjdint
695219089Spjdnvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp)
696219089Spjd{
697219089Spjd	if (nvl == NULL || nvp == NULL)
698219089Spjd		return (EINVAL);
699219089Spjd
700219089Spjd	nvp_buf_unlink(nvl, nvp);
701219089Spjd	nvpair_free(nvp);
702219089Spjd	nvp_buf_free(nvl, nvp);
703219089Spjd	return (0);
704219089Spjd}
705219089Spjd
706168404Spjd/*
707168404Spjd * This function calculates the size of an nvpair value.
708168404Spjd *
709168404Spjd * The data argument controls the behavior in case of the data types
710168404Spjd * 	DATA_TYPE_STRING    	and
711168404Spjd *	DATA_TYPE_STRING_ARRAY
712168404Spjd * Is data == NULL then the size of the string(s) is excluded.
713168404Spjd */
714168404Spjdstatic int
715168404Spjdi_get_value_size(data_type_t type, const void *data, uint_t nelem)
716168404Spjd{
717168404Spjd	uint64_t value_sz;
718168404Spjd
719168404Spjd	if (i_validate_type_nelem(type, nelem) != 0)
720168404Spjd		return (-1);
721168404Spjd
722168404Spjd	/* Calculate required size for holding value */
723168404Spjd	switch (type) {
724168404Spjd	case DATA_TYPE_BOOLEAN:
725168404Spjd		value_sz = 0;
726168404Spjd		break;
727168404Spjd	case DATA_TYPE_BOOLEAN_VALUE:
728168404Spjd		value_sz = sizeof (boolean_t);
729168404Spjd		break;
730168404Spjd	case DATA_TYPE_BYTE:
731168404Spjd		value_sz = sizeof (uchar_t);
732168404Spjd		break;
733168404Spjd	case DATA_TYPE_INT8:
734168404Spjd		value_sz = sizeof (int8_t);
735168404Spjd		break;
736168404Spjd	case DATA_TYPE_UINT8:
737168404Spjd		value_sz = sizeof (uint8_t);
738168404Spjd		break;
739168404Spjd	case DATA_TYPE_INT16:
740168404Spjd		value_sz = sizeof (int16_t);
741168404Spjd		break;
742168404Spjd	case DATA_TYPE_UINT16:
743168404Spjd		value_sz = sizeof (uint16_t);
744168404Spjd		break;
745168404Spjd	case DATA_TYPE_INT32:
746168404Spjd		value_sz = sizeof (int32_t);
747168404Spjd		break;
748168404Spjd	case DATA_TYPE_UINT32:
749168404Spjd		value_sz = sizeof (uint32_t);
750168404Spjd		break;
751168404Spjd	case DATA_TYPE_INT64:
752168404Spjd		value_sz = sizeof (int64_t);
753168404Spjd		break;
754168404Spjd	case DATA_TYPE_UINT64:
755168404Spjd		value_sz = sizeof (uint64_t);
756168404Spjd		break;
757185029Spjd#if !defined(_KERNEL)
758185029Spjd	case DATA_TYPE_DOUBLE:
759185029Spjd		value_sz = sizeof (double);
760185029Spjd		break;
761185029Spjd#endif
762168404Spjd	case DATA_TYPE_STRING:
763168404Spjd		if (data == NULL)
764168404Spjd			value_sz = 0;
765168404Spjd		else
766168404Spjd			value_sz = strlen(data) + 1;
767168404Spjd		break;
768168404Spjd	case DATA_TYPE_BOOLEAN_ARRAY:
769168404Spjd		value_sz = (uint64_t)nelem * sizeof (boolean_t);
770168404Spjd		break;
771168404Spjd	case DATA_TYPE_BYTE_ARRAY:
772168404Spjd		value_sz = (uint64_t)nelem * sizeof (uchar_t);
773168404Spjd		break;
774168404Spjd	case DATA_TYPE_INT8_ARRAY:
775168404Spjd		value_sz = (uint64_t)nelem * sizeof (int8_t);
776168404Spjd		break;
777168404Spjd	case DATA_TYPE_UINT8_ARRAY:
778168404Spjd		value_sz = (uint64_t)nelem * sizeof (uint8_t);
779168404Spjd		break;
780168404Spjd	case DATA_TYPE_INT16_ARRAY:
781168404Spjd		value_sz = (uint64_t)nelem * sizeof (int16_t);
782168404Spjd		break;
783168404Spjd	case DATA_TYPE_UINT16_ARRAY:
784168404Spjd		value_sz = (uint64_t)nelem * sizeof (uint16_t);
785168404Spjd		break;
786168404Spjd	case DATA_TYPE_INT32_ARRAY:
787168404Spjd		value_sz = (uint64_t)nelem * sizeof (int32_t);
788168404Spjd		break;
789168404Spjd	case DATA_TYPE_UINT32_ARRAY:
790168404Spjd		value_sz = (uint64_t)nelem * sizeof (uint32_t);
791168404Spjd		break;
792168404Spjd	case DATA_TYPE_INT64_ARRAY:
793168404Spjd		value_sz = (uint64_t)nelem * sizeof (int64_t);
794168404Spjd		break;
795168404Spjd	case DATA_TYPE_UINT64_ARRAY:
796168404Spjd		value_sz = (uint64_t)nelem * sizeof (uint64_t);
797168404Spjd		break;
798168404Spjd	case DATA_TYPE_STRING_ARRAY:
799168404Spjd		value_sz = (uint64_t)nelem * sizeof (uint64_t);
800168404Spjd
801168404Spjd		if (data != NULL) {
802168404Spjd			char *const *strs = data;
803168404Spjd			uint_t i;
804168404Spjd
805168404Spjd			/* no alignment requirement for strings */
806168404Spjd			for (i = 0; i < nelem; i++) {
807168404Spjd				if (strs[i] == NULL)
808168404Spjd					return (-1);
809168404Spjd				value_sz += strlen(strs[i]) + 1;
810168404Spjd			}
811168404Spjd		}
812168404Spjd		break;
813168404Spjd	case DATA_TYPE_HRTIME:
814168404Spjd		value_sz = sizeof (hrtime_t);
815168404Spjd		break;
816168404Spjd	case DATA_TYPE_NVLIST:
817168404Spjd		value_sz = NV_ALIGN(sizeof (nvlist_t));
818168404Spjd		break;
819168404Spjd	case DATA_TYPE_NVLIST_ARRAY:
820168404Spjd		value_sz = (uint64_t)nelem * sizeof (uint64_t) +
821168404Spjd		    (uint64_t)nelem * NV_ALIGN(sizeof (nvlist_t));
822168404Spjd		break;
823168404Spjd	default:
824168404Spjd		return (-1);
825168404Spjd	}
826168404Spjd
827168404Spjd	return (value_sz > INT32_MAX ? -1 : (int)value_sz);
828168404Spjd}
829168404Spjd
830168404Spjdstatic int
831168404Spjdnvlist_copy_embedded(nvlist_t *nvl, nvlist_t *onvl, nvlist_t *emb_nvl)
832168404Spjd{
833168404Spjd	nvpriv_t *priv;
834168404Spjd	int err;
835168404Spjd
836168404Spjd	if ((priv = nv_priv_alloc_embedded((nvpriv_t *)(uintptr_t)
837168404Spjd	    nvl->nvl_priv)) == NULL)
838168404Spjd		return (ENOMEM);
839168404Spjd
840168404Spjd	nvlist_init(emb_nvl, onvl->nvl_nvflag, priv);
841168404Spjd
842168404Spjd	if ((err = nvlist_copy_pairs(onvl, emb_nvl)) != 0) {
843168404Spjd		nvlist_free(emb_nvl);
844168404Spjd		emb_nvl->nvl_priv = 0;
845168404Spjd	}
846168404Spjd
847168404Spjd	return (err);
848168404Spjd}
849168404Spjd
850168404Spjd/*
851168404Spjd * nvlist_add_common - Add new <name,value> pair to nvlist
852168404Spjd */
853168404Spjdstatic int
854168404Spjdnvlist_add_common(nvlist_t *nvl, const char *name,
855168404Spjd    data_type_t type, uint_t nelem, const void *data)
856168404Spjd{
857168404Spjd	nvpair_t *nvp;
858168404Spjd	uint_t i;
859168404Spjd
860168404Spjd	int nvp_sz, name_sz, value_sz;
861168404Spjd	int err = 0;
862168404Spjd
863168404Spjd	if (name == NULL || nvl == NULL || nvl->nvl_priv == 0)
864168404Spjd		return (EINVAL);
865168404Spjd
866168404Spjd	if (nelem != 0 && data == NULL)
867168404Spjd		return (EINVAL);
868168404Spjd
869168404Spjd	/*
870168404Spjd	 * Verify type and nelem and get the value size.
871168404Spjd	 * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY
872168404Spjd	 * is the size of the string(s) included.
873168404Spjd	 */
874168404Spjd	if ((value_sz = i_get_value_size(type, data, nelem)) < 0)
875168404Spjd		return (EINVAL);
876168404Spjd
877168404Spjd	if (i_validate_nvpair_value(type, nelem, data) != 0)
878168404Spjd		return (EINVAL);
879168404Spjd
880168404Spjd	/*
881168404Spjd	 * If we're adding an nvlist or nvlist array, ensure that we are not
882168404Spjd	 * adding the input nvlist to itself, which would cause recursion,
883168404Spjd	 * and ensure that no NULL nvlist pointers are present.
884168404Spjd	 */
885168404Spjd	switch (type) {
886168404Spjd	case DATA_TYPE_NVLIST:
887168404Spjd		if (data == nvl || data == NULL)
888168404Spjd			return (EINVAL);
889168404Spjd		break;
890168404Spjd	case DATA_TYPE_NVLIST_ARRAY: {
891168404Spjd		nvlist_t **onvlp = (nvlist_t **)data;
892168404Spjd		for (i = 0; i < nelem; i++) {
893168404Spjd			if (onvlp[i] == nvl || onvlp[i] == NULL)
894168404Spjd				return (EINVAL);
895168404Spjd		}
896168404Spjd		break;
897168404Spjd	}
898168404Spjd	default:
899168404Spjd		break;
900168404Spjd	}
901168404Spjd
902168404Spjd	/* calculate sizes of the nvpair elements and the nvpair itself */
903168404Spjd	name_sz = strlen(name) + 1;
904168404Spjd
905168404Spjd	nvp_sz = NVP_SIZE_CALC(name_sz, value_sz);
906168404Spjd
907168404Spjd	if ((nvp = nvp_buf_alloc(nvl, nvp_sz)) == NULL)
908168404Spjd		return (ENOMEM);
909168404Spjd
910168404Spjd	ASSERT(nvp->nvp_size == nvp_sz);
911168404Spjd	nvp->nvp_name_sz = name_sz;
912168404Spjd	nvp->nvp_value_elem = nelem;
913168404Spjd	nvp->nvp_type = type;
914168404Spjd	bcopy(name, NVP_NAME(nvp), name_sz);
915168404Spjd
916168404Spjd	switch (type) {
917168404Spjd	case DATA_TYPE_BOOLEAN:
918168404Spjd		break;
919168404Spjd	case DATA_TYPE_STRING_ARRAY: {
920168404Spjd		char *const *strs = data;
921168404Spjd		char *buf = NVP_VALUE(nvp);
922168404Spjd		char **cstrs = (void *)buf;
923168404Spjd
924168404Spjd		/* skip pre-allocated space for pointer array */
925168404Spjd		buf += nelem * sizeof (uint64_t);
926168404Spjd		for (i = 0; i < nelem; i++) {
927168404Spjd			int slen = strlen(strs[i]) + 1;
928168404Spjd			bcopy(strs[i], buf, slen);
929168404Spjd			cstrs[i] = buf;
930168404Spjd			buf += slen;
931168404Spjd		}
932168404Spjd		break;
933168404Spjd	}
934168404Spjd	case DATA_TYPE_NVLIST: {
935168404Spjd		nvlist_t *nnvl = EMBEDDED_NVL(nvp);
936168404Spjd		nvlist_t *onvl = (nvlist_t *)data;
937168404Spjd
938168404Spjd		if ((err = nvlist_copy_embedded(nvl, onvl, nnvl)) != 0) {
939168404Spjd			nvp_buf_free(nvl, nvp);
940168404Spjd			return (err);
941168404Spjd		}
942168404Spjd		break;
943168404Spjd	}
944168404Spjd	case DATA_TYPE_NVLIST_ARRAY: {
945168404Spjd		nvlist_t **onvlp = (nvlist_t **)data;
946168404Spjd		nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp);
947168404Spjd		nvlist_t *embedded = (nvlist_t *)
948168404Spjd		    ((uintptr_t)nvlp + nelem * sizeof (uint64_t));
949168404Spjd
950168404Spjd		for (i = 0; i < nelem; i++) {
951168404Spjd			if ((err = nvlist_copy_embedded(nvl,
952168404Spjd			    onvlp[i], embedded)) != 0) {
953168404Spjd				/*
954168404Spjd				 * Free any successfully created lists
955168404Spjd				 */
956168404Spjd				nvpair_free(nvp);
957168404Spjd				nvp_buf_free(nvl, nvp);
958168404Spjd				return (err);
959168404Spjd			}
960168404Spjd
961168404Spjd			nvlp[i] = embedded++;
962168404Spjd		}
963168404Spjd		break;
964168404Spjd	}
965168404Spjd	default:
966168404Spjd		bcopy(data, NVP_VALUE(nvp), value_sz);
967168404Spjd	}
968168404Spjd
969168404Spjd	/* if unique name, remove before add */
970168404Spjd	if (nvl->nvl_nvflag & NV_UNIQUE_NAME)
971168404Spjd		(void) nvlist_remove_all(nvl, name);
972168404Spjd	else if (nvl->nvl_nvflag & NV_UNIQUE_NAME_TYPE)
973168404Spjd		(void) nvlist_remove(nvl, name, type);
974168404Spjd
975168404Spjd	nvp_buf_link(nvl, nvp);
976168404Spjd
977168404Spjd	return (0);
978168404Spjd}
979168404Spjd
980168404Spjdint
981168404Spjdnvlist_add_boolean(nvlist_t *nvl, const char *name)
982168404Spjd{
983168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN, 0, NULL));
984168404Spjd}
985168404Spjd
986168404Spjdint
987168404Spjdnvlist_add_boolean_value(nvlist_t *nvl, const char *name, boolean_t val)
988168404Spjd{
989168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, 1, &val));
990168404Spjd}
991168404Spjd
992168404Spjdint
993168404Spjdnvlist_add_byte(nvlist_t *nvl, const char *name, uchar_t val)
994168404Spjd{
995168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE, 1, &val));
996168404Spjd}
997168404Spjd
998168404Spjdint
999168404Spjdnvlist_add_int8(nvlist_t *nvl, const char *name, int8_t val)
1000168404Spjd{
1001168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT8, 1, &val));
1002168404Spjd}
1003168404Spjd
1004168404Spjdint
1005168404Spjdnvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t val)
1006168404Spjd{
1007168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8, 1, &val));
1008168404Spjd}
1009168404Spjd
1010168404Spjdint
1011168404Spjdnvlist_add_int16(nvlist_t *nvl, const char *name, int16_t val)
1012168404Spjd{
1013168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT16, 1, &val));
1014168404Spjd}
1015168404Spjd
1016168404Spjdint
1017168404Spjdnvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t val)
1018168404Spjd{
1019168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16, 1, &val));
1020168404Spjd}
1021168404Spjd
1022168404Spjdint
1023168404Spjdnvlist_add_int32(nvlist_t *nvl, const char *name, int32_t val)
1024168404Spjd{
1025168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT32, 1, &val));
1026168404Spjd}
1027168404Spjd
1028168404Spjdint
1029168404Spjdnvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t val)
1030168404Spjd{
1031168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32, 1, &val));
1032168404Spjd}
1033168404Spjd
1034168404Spjdint
1035168404Spjdnvlist_add_int64(nvlist_t *nvl, const char *name, int64_t val)
1036168404Spjd{
1037168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT64, 1, &val));
1038168404Spjd}
1039168404Spjd
1040168404Spjdint
1041168404Spjdnvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t val)
1042168404Spjd{
1043168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &val));
1044168404Spjd}
1045168404Spjd
1046185029Spjd#if !defined(_KERNEL)
1047168404Spjdint
1048185029Spjdnvlist_add_double(nvlist_t *nvl, const char *name, double val)
1049185029Spjd{
1050185029Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_DOUBLE, 1, &val));
1051185029Spjd}
1052185029Spjd#endif
1053185029Spjd
1054185029Spjdint
1055168404Spjdnvlist_add_string(nvlist_t *nvl, const char *name, const char *val)
1056168404Spjd{
1057168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_STRING, 1, (void *)val));
1058168404Spjd}
1059168404Spjd
1060168404Spjdint
1061168404Spjdnvlist_add_boolean_array(nvlist_t *nvl, const char *name,
1062168404Spjd    boolean_t *a, uint_t n)
1063168404Spjd{
1064168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_ARRAY, n, a));
1065168404Spjd}
1066168404Spjd
1067168404Spjdint
1068168404Spjdnvlist_add_byte_array(nvlist_t *nvl, const char *name, uchar_t *a, uint_t n)
1069168404Spjd{
1070168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a));
1071168404Spjd}
1072168404Spjd
1073168404Spjdint
1074168404Spjdnvlist_add_int8_array(nvlist_t *nvl, const char *name, int8_t *a, uint_t n)
1075168404Spjd{
1076168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a));
1077168404Spjd}
1078168404Spjd
1079168404Spjdint
1080168404Spjdnvlist_add_uint8_array(nvlist_t *nvl, const char *name, uint8_t *a, uint_t n)
1081168404Spjd{
1082168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a));
1083168404Spjd}
1084168404Spjd
1085168404Spjdint
1086168404Spjdnvlist_add_int16_array(nvlist_t *nvl, const char *name, int16_t *a, uint_t n)
1087168404Spjd{
1088168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a));
1089168404Spjd}
1090168404Spjd
1091168404Spjdint
1092168404Spjdnvlist_add_uint16_array(nvlist_t *nvl, const char *name, uint16_t *a, uint_t n)
1093168404Spjd{
1094168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a));
1095168404Spjd}
1096168404Spjd
1097168404Spjdint
1098168404Spjdnvlist_add_int32_array(nvlist_t *nvl, const char *name, int32_t *a, uint_t n)
1099168404Spjd{
1100168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a));
1101168404Spjd}
1102168404Spjd
1103168404Spjdint
1104168404Spjdnvlist_add_uint32_array(nvlist_t *nvl, const char *name, uint32_t *a, uint_t n)
1105168404Spjd{
1106168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a));
1107168404Spjd}
1108168404Spjd
1109168404Spjdint
1110168404Spjdnvlist_add_int64_array(nvlist_t *nvl, const char *name, int64_t *a, uint_t n)
1111168404Spjd{
1112168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a));
1113168404Spjd}
1114168404Spjd
1115168404Spjdint
1116168404Spjdnvlist_add_uint64_array(nvlist_t *nvl, const char *name, uint64_t *a, uint_t n)
1117168404Spjd{
1118168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a));
1119168404Spjd}
1120168404Spjd
1121168404Spjdint
1122168404Spjdnvlist_add_string_array(nvlist_t *nvl, const char *name,
1123168404Spjd    char *const *a, uint_t n)
1124168404Spjd{
1125168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a));
1126168404Spjd}
1127168404Spjd
1128168404Spjdint
1129168404Spjdnvlist_add_hrtime(nvlist_t *nvl, const char *name, hrtime_t val)
1130168404Spjd{
1131168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_HRTIME, 1, &val));
1132168404Spjd}
1133168404Spjd
1134168404Spjdint
1135168404Spjdnvlist_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val)
1136168404Spjd{
1137168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST, 1, val));
1138168404Spjd}
1139168404Spjd
1140168404Spjdint
1141168404Spjdnvlist_add_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **a, uint_t n)
1142168404Spjd{
1143168404Spjd	return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a));
1144168404Spjd}
1145168404Spjd
1146168404Spjd/* reading name-value pairs */
1147168404Spjdnvpair_t *
1148168404Spjdnvlist_next_nvpair(nvlist_t *nvl, nvpair_t *nvp)
1149168404Spjd{
1150168404Spjd	nvpriv_t *priv;
1151168404Spjd	i_nvp_t *curr;
1152168404Spjd
1153168404Spjd	if (nvl == NULL ||
1154168404Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
1155168404Spjd		return (NULL);
1156168404Spjd
1157168404Spjd	curr = NVPAIR2I_NVP(nvp);
1158168404Spjd
1159168404Spjd	/*
1160185029Spjd	 * Ensure that nvp is a valid nvpair on this nvlist.
1161185029Spjd	 * NB: nvp_curr is used only as a hint so that we don't always
1162185029Spjd	 * have to walk the list to determine if nvp is still on the list.
1163168404Spjd	 */
1164168404Spjd	if (nvp == NULL)
1165168404Spjd		curr = priv->nvp_list;
1166185029Spjd	else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp))
1167168404Spjd		curr = curr->nvi_next;
1168185029Spjd	else
1169168404Spjd		curr = NULL;
1170168404Spjd
1171168404Spjd	priv->nvp_curr = curr;
1172168404Spjd
1173168404Spjd	return (curr != NULL ? &curr->nvi_nvp : NULL);
1174168404Spjd}
1175168404Spjd
1176219089Spjdnvpair_t *
1177219089Spjdnvlist_prev_nvpair(nvlist_t *nvl, nvpair_t *nvp)
1178219089Spjd{
1179219089Spjd	nvpriv_t *priv;
1180219089Spjd	i_nvp_t *curr;
1181219089Spjd
1182219089Spjd	if (nvl == NULL ||
1183219089Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
1184219089Spjd		return (NULL);
1185219089Spjd
1186219089Spjd	curr = NVPAIR2I_NVP(nvp);
1187219089Spjd
1188219089Spjd	if (nvp == NULL)
1189219089Spjd		curr = priv->nvp_last;
1190219089Spjd	else if (priv->nvp_curr == curr || nvlist_contains_nvp(nvl, nvp))
1191219089Spjd		curr = curr->nvi_prev;
1192219089Spjd	else
1193219089Spjd		curr = NULL;
1194219089Spjd
1195219089Spjd	priv->nvp_curr = curr;
1196219089Spjd
1197219089Spjd	return (curr != NULL ? &curr->nvi_nvp : NULL);
1198219089Spjd}
1199219089Spjd
1200219089Spjdboolean_t
1201219089Spjdnvlist_empty(nvlist_t *nvl)
1202219089Spjd{
1203219089Spjd	nvpriv_t *priv;
1204219089Spjd
1205219089Spjd	if (nvl == NULL ||
1206219089Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
1207219089Spjd		return (B_TRUE);
1208219089Spjd
1209219089Spjd	return (priv->nvp_list == NULL);
1210219089Spjd}
1211219089Spjd
1212168404Spjdchar *
1213168404Spjdnvpair_name(nvpair_t *nvp)
1214168404Spjd{
1215168404Spjd	return (NVP_NAME(nvp));
1216168404Spjd}
1217168404Spjd
1218168404Spjddata_type_t
1219168404Spjdnvpair_type(nvpair_t *nvp)
1220168404Spjd{
1221168404Spjd	return (NVP_TYPE(nvp));
1222168404Spjd}
1223168404Spjd
1224185029Spjdint
1225185029Spjdnvpair_type_is_array(nvpair_t *nvp)
1226185029Spjd{
1227185029Spjd	data_type_t type = NVP_TYPE(nvp);
1228185029Spjd
1229185029Spjd	if ((type == DATA_TYPE_BYTE_ARRAY) ||
1230185029Spjd	    (type == DATA_TYPE_UINT8_ARRAY) ||
1231185029Spjd	    (type == DATA_TYPE_INT16_ARRAY) ||
1232185029Spjd	    (type == DATA_TYPE_UINT16_ARRAY) ||
1233185029Spjd	    (type == DATA_TYPE_INT32_ARRAY) ||
1234185029Spjd	    (type == DATA_TYPE_UINT32_ARRAY) ||
1235185029Spjd	    (type == DATA_TYPE_INT64_ARRAY) ||
1236185029Spjd	    (type == DATA_TYPE_UINT64_ARRAY) ||
1237185029Spjd	    (type == DATA_TYPE_BOOLEAN_ARRAY) ||
1238185029Spjd	    (type == DATA_TYPE_STRING_ARRAY) ||
1239185029Spjd	    (type == DATA_TYPE_NVLIST_ARRAY))
1240185029Spjd		return (1);
1241185029Spjd	return (0);
1242185029Spjd
1243185029Spjd}
1244185029Spjd
1245168404Spjdstatic int
1246168404Spjdnvpair_value_common(nvpair_t *nvp, data_type_t type, uint_t *nelem, void *data)
1247168404Spjd{
1248168404Spjd	if (nvp == NULL || nvpair_type(nvp) != type)
1249168404Spjd		return (EINVAL);
1250168404Spjd
1251168404Spjd	/*
1252168404Spjd	 * For non-array types, we copy the data.
1253168404Spjd	 * For array types (including string), we set a pointer.
1254168404Spjd	 */
1255168404Spjd	switch (type) {
1256168404Spjd	case DATA_TYPE_BOOLEAN:
1257168404Spjd		if (nelem != NULL)
1258168404Spjd			*nelem = 0;
1259168404Spjd		break;
1260168404Spjd
1261168404Spjd	case DATA_TYPE_BOOLEAN_VALUE:
1262168404Spjd	case DATA_TYPE_BYTE:
1263168404Spjd	case DATA_TYPE_INT8:
1264168404Spjd	case DATA_TYPE_UINT8:
1265168404Spjd	case DATA_TYPE_INT16:
1266168404Spjd	case DATA_TYPE_UINT16:
1267168404Spjd	case DATA_TYPE_INT32:
1268168404Spjd	case DATA_TYPE_UINT32:
1269168404Spjd	case DATA_TYPE_INT64:
1270168404Spjd	case DATA_TYPE_UINT64:
1271168404Spjd	case DATA_TYPE_HRTIME:
1272185029Spjd#if !defined(_KERNEL)
1273185029Spjd	case DATA_TYPE_DOUBLE:
1274185029Spjd#endif
1275168404Spjd		if (data == NULL)
1276168404Spjd			return (EINVAL);
1277168404Spjd		bcopy(NVP_VALUE(nvp), data,
1278168404Spjd		    (size_t)i_get_value_size(type, NULL, 1));
1279168404Spjd		if (nelem != NULL)
1280168404Spjd			*nelem = 1;
1281168404Spjd		break;
1282168404Spjd
1283168404Spjd	case DATA_TYPE_NVLIST:
1284168404Spjd	case DATA_TYPE_STRING:
1285168404Spjd		if (data == NULL)
1286168404Spjd			return (EINVAL);
1287168404Spjd		*(void **)data = (void *)NVP_VALUE(nvp);
1288168404Spjd		if (nelem != NULL)
1289168404Spjd			*nelem = 1;
1290168404Spjd		break;
1291168404Spjd
1292168404Spjd	case DATA_TYPE_BOOLEAN_ARRAY:
1293168404Spjd	case DATA_TYPE_BYTE_ARRAY:
1294168404Spjd	case DATA_TYPE_INT8_ARRAY:
1295168404Spjd	case DATA_TYPE_UINT8_ARRAY:
1296168404Spjd	case DATA_TYPE_INT16_ARRAY:
1297168404Spjd	case DATA_TYPE_UINT16_ARRAY:
1298168404Spjd	case DATA_TYPE_INT32_ARRAY:
1299168404Spjd	case DATA_TYPE_UINT32_ARRAY:
1300168404Spjd	case DATA_TYPE_INT64_ARRAY:
1301168404Spjd	case DATA_TYPE_UINT64_ARRAY:
1302168404Spjd	case DATA_TYPE_STRING_ARRAY:
1303168404Spjd	case DATA_TYPE_NVLIST_ARRAY:
1304168404Spjd		if (nelem == NULL || data == NULL)
1305168404Spjd			return (EINVAL);
1306168404Spjd		if ((*nelem = NVP_NELEM(nvp)) != 0)
1307168404Spjd			*(void **)data = (void *)NVP_VALUE(nvp);
1308168404Spjd		else
1309168404Spjd			*(void **)data = NULL;
1310168404Spjd		break;
1311168404Spjd
1312168404Spjd	default:
1313168404Spjd		return (ENOTSUP);
1314168404Spjd	}
1315168404Spjd
1316168404Spjd	return (0);
1317168404Spjd}
1318168404Spjd
1319168404Spjdstatic int
1320168404Spjdnvlist_lookup_common(nvlist_t *nvl, const char *name, data_type_t type,
1321168404Spjd    uint_t *nelem, void *data)
1322168404Spjd{
1323168404Spjd	nvpriv_t *priv;
1324168404Spjd	nvpair_t *nvp;
1325168404Spjd	i_nvp_t *curr;
1326168404Spjd
1327168404Spjd	if (name == NULL || nvl == NULL ||
1328168404Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
1329168404Spjd		return (EINVAL);
1330168404Spjd
1331168404Spjd	if (!(nvl->nvl_nvflag & (NV_UNIQUE_NAME | NV_UNIQUE_NAME_TYPE)))
1332168404Spjd		return (ENOTSUP);
1333168404Spjd
1334168404Spjd	for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
1335168404Spjd		nvp = &curr->nvi_nvp;
1336168404Spjd
1337168404Spjd		if (strcmp(name, NVP_NAME(nvp)) == 0 && NVP_TYPE(nvp) == type)
1338168404Spjd			return (nvpair_value_common(nvp, type, nelem, data));
1339168404Spjd	}
1340168404Spjd
1341168404Spjd	return (ENOENT);
1342168404Spjd}
1343168404Spjd
1344168404Spjdint
1345168404Spjdnvlist_lookup_boolean(nvlist_t *nvl, const char *name)
1346168404Spjd{
1347168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_BOOLEAN, NULL, NULL));
1348168404Spjd}
1349168404Spjd
1350168404Spjdint
1351168404Spjdnvlist_lookup_boolean_value(nvlist_t *nvl, const char *name, boolean_t *val)
1352168404Spjd{
1353168404Spjd	return (nvlist_lookup_common(nvl, name,
1354168404Spjd	    DATA_TYPE_BOOLEAN_VALUE, NULL, val));
1355168404Spjd}
1356168404Spjd
1357168404Spjdint
1358168404Spjdnvlist_lookup_byte(nvlist_t *nvl, const char *name, uchar_t *val)
1359168404Spjd{
1360168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE, NULL, val));
1361168404Spjd}
1362168404Spjd
1363168404Spjdint
1364168404Spjdnvlist_lookup_int8(nvlist_t *nvl, const char *name, int8_t *val)
1365168404Spjd{
1366168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8, NULL, val));
1367168404Spjd}
1368168404Spjd
1369168404Spjdint
1370168404Spjdnvlist_lookup_uint8(nvlist_t *nvl, const char *name, uint8_t *val)
1371168404Spjd{
1372168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8, NULL, val));
1373168404Spjd}
1374168404Spjd
1375168404Spjdint
1376168404Spjdnvlist_lookup_int16(nvlist_t *nvl, const char *name, int16_t *val)
1377168404Spjd{
1378168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16, NULL, val));
1379168404Spjd}
1380168404Spjd
1381168404Spjdint
1382168404Spjdnvlist_lookup_uint16(nvlist_t *nvl, const char *name, uint16_t *val)
1383168404Spjd{
1384168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16, NULL, val));
1385168404Spjd}
1386168404Spjd
1387168404Spjdint
1388168404Spjdnvlist_lookup_int32(nvlist_t *nvl, const char *name, int32_t *val)
1389168404Spjd{
1390168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32, NULL, val));
1391168404Spjd}
1392168404Spjd
1393168404Spjdint
1394168404Spjdnvlist_lookup_uint32(nvlist_t *nvl, const char *name, uint32_t *val)
1395168404Spjd{
1396168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32, NULL, val));
1397168404Spjd}
1398168404Spjd
1399168404Spjdint
1400168404Spjdnvlist_lookup_int64(nvlist_t *nvl, const char *name, int64_t *val)
1401168404Spjd{
1402168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64, NULL, val));
1403168404Spjd}
1404168404Spjd
1405168404Spjdint
1406168404Spjdnvlist_lookup_uint64(nvlist_t *nvl, const char *name, uint64_t *val)
1407168404Spjd{
1408168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64, NULL, val));
1409168404Spjd}
1410168404Spjd
1411185029Spjd#if !defined(_KERNEL)
1412168404Spjdint
1413185029Spjdnvlist_lookup_double(nvlist_t *nvl, const char *name, double *val)
1414185029Spjd{
1415185029Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_DOUBLE, NULL, val));
1416185029Spjd}
1417185029Spjd#endif
1418185029Spjd
1419185029Spjdint
1420168404Spjdnvlist_lookup_string(nvlist_t *nvl, const char *name, char **val)
1421168404Spjd{
1422168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING, NULL, val));
1423168404Spjd}
1424168404Spjd
1425168404Spjdint
1426168404Spjdnvlist_lookup_nvlist(nvlist_t *nvl, const char *name, nvlist_t **val)
1427168404Spjd{
1428168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST, NULL, val));
1429168404Spjd}
1430168404Spjd
1431168404Spjdint
1432168404Spjdnvlist_lookup_boolean_array(nvlist_t *nvl, const char *name,
1433168404Spjd    boolean_t **a, uint_t *n)
1434168404Spjd{
1435168404Spjd	return (nvlist_lookup_common(nvl, name,
1436168404Spjd	    DATA_TYPE_BOOLEAN_ARRAY, n, a));
1437168404Spjd}
1438168404Spjd
1439168404Spjdint
1440168404Spjdnvlist_lookup_byte_array(nvlist_t *nvl, const char *name,
1441168404Spjd    uchar_t **a, uint_t *n)
1442168404Spjd{
1443168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a));
1444168404Spjd}
1445168404Spjd
1446168404Spjdint
1447168404Spjdnvlist_lookup_int8_array(nvlist_t *nvl, const char *name, int8_t **a, uint_t *n)
1448168404Spjd{
1449168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a));
1450168404Spjd}
1451168404Spjd
1452168404Spjdint
1453168404Spjdnvlist_lookup_uint8_array(nvlist_t *nvl, const char *name,
1454168404Spjd    uint8_t **a, uint_t *n)
1455168404Spjd{
1456168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a));
1457168404Spjd}
1458168404Spjd
1459168404Spjdint
1460168404Spjdnvlist_lookup_int16_array(nvlist_t *nvl, const char *name,
1461168404Spjd    int16_t **a, uint_t *n)
1462168404Spjd{
1463168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a));
1464168404Spjd}
1465168404Spjd
1466168404Spjdint
1467168404Spjdnvlist_lookup_uint16_array(nvlist_t *nvl, const char *name,
1468168404Spjd    uint16_t **a, uint_t *n)
1469168404Spjd{
1470168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a));
1471168404Spjd}
1472168404Spjd
1473168404Spjdint
1474168404Spjdnvlist_lookup_int32_array(nvlist_t *nvl, const char *name,
1475168404Spjd    int32_t **a, uint_t *n)
1476168404Spjd{
1477168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a));
1478168404Spjd}
1479168404Spjd
1480168404Spjdint
1481168404Spjdnvlist_lookup_uint32_array(nvlist_t *nvl, const char *name,
1482168404Spjd    uint32_t **a, uint_t *n)
1483168404Spjd{
1484168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a));
1485168404Spjd}
1486168404Spjd
1487168404Spjdint
1488168404Spjdnvlist_lookup_int64_array(nvlist_t *nvl, const char *name,
1489168404Spjd    int64_t **a, uint_t *n)
1490168404Spjd{
1491168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a));
1492168404Spjd}
1493168404Spjd
1494168404Spjdint
1495168404Spjdnvlist_lookup_uint64_array(nvlist_t *nvl, const char *name,
1496168404Spjd    uint64_t **a, uint_t *n)
1497168404Spjd{
1498168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a));
1499168404Spjd}
1500168404Spjd
1501168404Spjdint
1502168404Spjdnvlist_lookup_string_array(nvlist_t *nvl, const char *name,
1503168404Spjd    char ***a, uint_t *n)
1504168404Spjd{
1505168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a));
1506168404Spjd}
1507168404Spjd
1508168404Spjdint
1509168404Spjdnvlist_lookup_nvlist_array(nvlist_t *nvl, const char *name,
1510168404Spjd    nvlist_t ***a, uint_t *n)
1511168404Spjd{
1512168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a));
1513168404Spjd}
1514168404Spjd
1515168404Spjdint
1516168404Spjdnvlist_lookup_hrtime(nvlist_t *nvl, const char *name, hrtime_t *val)
1517168404Spjd{
1518168404Spjd	return (nvlist_lookup_common(nvl, name, DATA_TYPE_HRTIME, NULL, val));
1519168404Spjd}
1520168404Spjd
1521168404Spjdint
1522168404Spjdnvlist_lookup_pairs(nvlist_t *nvl, int flag, ...)
1523168404Spjd{
1524168404Spjd	va_list ap;
1525168404Spjd	char *name;
1526168404Spjd	int noentok = (flag & NV_FLAG_NOENTOK ? 1 : 0);
1527168404Spjd	int ret = 0;
1528168404Spjd
1529168404Spjd	va_start(ap, flag);
1530168404Spjd	while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
1531168404Spjd		data_type_t type;
1532168404Spjd		void *val;
1533168404Spjd		uint_t *nelem;
1534168404Spjd
1535168404Spjd		switch (type = va_arg(ap, data_type_t)) {
1536168404Spjd		case DATA_TYPE_BOOLEAN:
1537168404Spjd			ret = nvlist_lookup_common(nvl, name, type, NULL, NULL);
1538168404Spjd			break;
1539168404Spjd
1540168404Spjd		case DATA_TYPE_BOOLEAN_VALUE:
1541168404Spjd		case DATA_TYPE_BYTE:
1542168404Spjd		case DATA_TYPE_INT8:
1543168404Spjd		case DATA_TYPE_UINT8:
1544168404Spjd		case DATA_TYPE_INT16:
1545168404Spjd		case DATA_TYPE_UINT16:
1546168404Spjd		case DATA_TYPE_INT32:
1547168404Spjd		case DATA_TYPE_UINT32:
1548168404Spjd		case DATA_TYPE_INT64:
1549168404Spjd		case DATA_TYPE_UINT64:
1550168404Spjd		case DATA_TYPE_HRTIME:
1551168404Spjd		case DATA_TYPE_STRING:
1552168404Spjd		case DATA_TYPE_NVLIST:
1553185029Spjd#if !defined(_KERNEL)
1554185029Spjd		case DATA_TYPE_DOUBLE:
1555185029Spjd#endif
1556168404Spjd			val = va_arg(ap, void *);
1557168404Spjd			ret = nvlist_lookup_common(nvl, name, type, NULL, val);
1558168404Spjd			break;
1559168404Spjd
1560168404Spjd		case DATA_TYPE_BYTE_ARRAY:
1561168404Spjd		case DATA_TYPE_BOOLEAN_ARRAY:
1562168404Spjd		case DATA_TYPE_INT8_ARRAY:
1563168404Spjd		case DATA_TYPE_UINT8_ARRAY:
1564168404Spjd		case DATA_TYPE_INT16_ARRAY:
1565168404Spjd		case DATA_TYPE_UINT16_ARRAY:
1566168404Spjd		case DATA_TYPE_INT32_ARRAY:
1567168404Spjd		case DATA_TYPE_UINT32_ARRAY:
1568168404Spjd		case DATA_TYPE_INT64_ARRAY:
1569168404Spjd		case DATA_TYPE_UINT64_ARRAY:
1570168404Spjd		case DATA_TYPE_STRING_ARRAY:
1571168404Spjd		case DATA_TYPE_NVLIST_ARRAY:
1572168404Spjd			val = va_arg(ap, void *);
1573168404Spjd			nelem = va_arg(ap, uint_t *);
1574168404Spjd			ret = nvlist_lookup_common(nvl, name, type, nelem, val);
1575168404Spjd			break;
1576168404Spjd
1577168404Spjd		default:
1578168404Spjd			ret = EINVAL;
1579168404Spjd		}
1580168404Spjd
1581168404Spjd		if (ret == ENOENT && noentok)
1582168404Spjd			ret = 0;
1583168404Spjd	}
1584168404Spjd	va_end(ap);
1585168404Spjd
1586168404Spjd	return (ret);
1587168404Spjd}
1588168404Spjd
1589185029Spjd/*
1590185029Spjd * Find the 'name'ed nvpair in the nvlist 'nvl'. If 'name' found, the function
1591185029Spjd * returns zero and a pointer to the matching nvpair is returned in '*ret'
1592185029Spjd * (given 'ret' is non-NULL). If 'sep' is specified then 'name' will penitrate
1593185029Spjd * multiple levels of embedded nvlists, with 'sep' as the separator. As an
1594185029Spjd * example, if sep is '.', name might look like: "a" or "a.b" or "a.c[3]" or
1595185029Spjd * "a.d[3].e[1]".  This matches the C syntax for array embed (for convience,
1596185029Spjd * code also supports "a.d[3]e[1]" syntax).
1597185029Spjd *
1598185029Spjd * If 'ip' is non-NULL and the last name component is an array, return the
1599185029Spjd * value of the "...[index]" array index in *ip. For an array reference that
1600185029Spjd * is not indexed, *ip will be returned as -1. If there is a syntax error in
1601185029Spjd * 'name', and 'ep' is non-NULL then *ep will be set to point to the location
1602185029Spjd * inside the 'name' string where the syntax error was detected.
1603185029Spjd */
1604185029Spjdstatic int
1605185029Spjdnvlist_lookup_nvpair_ei_sep(nvlist_t *nvl, const char *name, const char sep,
1606185029Spjd    nvpair_t **ret, int *ip, char **ep)
1607185029Spjd{
1608185029Spjd	nvpair_t	*nvp;
1609185029Spjd	const char	*np;
1610185029Spjd	char		*sepp;
1611185029Spjd	char		*idxp, *idxep;
1612185029Spjd	nvlist_t	**nva;
1613185029Spjd	long		idx;
1614185029Spjd	int		n;
1615185029Spjd
1616185029Spjd	if (ip)
1617185029Spjd		*ip = -1;			/* not indexed */
1618185029Spjd	if (ep)
1619185029Spjd		*ep = NULL;
1620185029Spjd
1621185029Spjd	if ((nvl == NULL) || (name == NULL))
1622185029Spjd		return (EINVAL);
1623185029Spjd
1624185029Spjd	/* step through components of name */
1625185029Spjd	for (np = name; np && *np; np = sepp) {
1626185029Spjd		/* ensure unique names */
1627185029Spjd		if (!(nvl->nvl_nvflag & NV_UNIQUE_NAME))
1628185029Spjd			return (ENOTSUP);
1629185029Spjd
1630185029Spjd		/* skip white space */
1631185029Spjd		skip_whitespace(np);
1632185029Spjd		if (*np == 0)
1633185029Spjd			break;
1634185029Spjd
1635185029Spjd		/* set 'sepp' to end of current component 'np' */
1636185029Spjd		if (sep)
1637185029Spjd			sepp = strchr(np, sep);
1638185029Spjd		else
1639185029Spjd			sepp = NULL;
1640185029Spjd
1641185029Spjd		/* find start of next "[ index ]..." */
1642185029Spjd		idxp = strchr(np, '[');
1643185029Spjd
1644185029Spjd		/* if sepp comes first, set idxp to NULL */
1645185029Spjd		if (sepp && idxp && (sepp < idxp))
1646185029Spjd			idxp = NULL;
1647185029Spjd
1648185029Spjd		/*
1649185029Spjd		 * At this point 'idxp' is set if there is an index
1650185029Spjd		 * expected for the current component.
1651185029Spjd		 */
1652185029Spjd		if (idxp) {
1653185029Spjd			/* set 'n' to length of current 'np' name component */
1654185029Spjd			n = idxp++ - np;
1655185029Spjd
1656185029Spjd			/* keep sepp up to date for *ep use as we advance */
1657185029Spjd			skip_whitespace(idxp);
1658185029Spjd			sepp = idxp;
1659185029Spjd
1660185029Spjd			/* determine the index value */
1661185029Spjd#if defined(_KERNEL) && !defined(_BOOT)
1662185029Spjd			if (ddi_strtol(idxp, &idxep, 0, &idx))
1663185029Spjd				goto fail;
1664185029Spjd#else
1665185029Spjd			idx = strtol(idxp, &idxep, 0);
1666185029Spjd#endif
1667185029Spjd			if (idxep == idxp)
1668185029Spjd				goto fail;
1669185029Spjd
1670185029Spjd			/* keep sepp up to date for *ep use as we advance */
1671185029Spjd			sepp = idxep;
1672185029Spjd
1673185029Spjd			/* skip white space index value and check for ']' */
1674185029Spjd			skip_whitespace(sepp);
1675185029Spjd			if (*sepp++ != ']')
1676185029Spjd				goto fail;
1677185029Spjd
1678185029Spjd			/* for embedded arrays, support C syntax: "a[1].b" */
1679185029Spjd			skip_whitespace(sepp);
1680185029Spjd			if (sep && (*sepp == sep))
1681185029Spjd				sepp++;
1682185029Spjd		} else if (sepp) {
1683185029Spjd			n = sepp++ - np;
1684185029Spjd		} else {
1685185029Spjd			n = strlen(np);
1686185029Spjd		}
1687185029Spjd
1688185029Spjd		/* trim trailing whitespace by reducing length of 'np' */
1689185029Spjd		if (n == 0)
1690185029Spjd			goto fail;
1691185029Spjd		for (n--; (np[n] == ' ') || (np[n] == '\t'); n--)
1692185029Spjd			;
1693185029Spjd		n++;
1694185029Spjd
1695185029Spjd		/* skip whitespace, and set sepp to NULL if complete */
1696185029Spjd		if (sepp) {
1697185029Spjd			skip_whitespace(sepp);
1698185029Spjd			if (*sepp == 0)
1699185029Spjd				sepp = NULL;
1700185029Spjd		}
1701185029Spjd
1702185029Spjd		/*
1703185029Spjd		 * At this point:
1704185029Spjd		 * o  'n' is the length of current 'np' component.
1705185029Spjd		 * o  'idxp' is set if there was an index, and value 'idx'.
1706185029Spjd		 * o  'sepp' is set to the beginning of the next component,
1707185029Spjd		 *    and set to NULL if we have no more components.
1708185029Spjd		 *
1709185029Spjd		 * Search for nvpair with matching component name.
1710185029Spjd		 */
1711185029Spjd		for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
1712185029Spjd		    nvp = nvlist_next_nvpair(nvl, nvp)) {
1713185029Spjd
1714185029Spjd			/* continue if no match on name */
1715185029Spjd			if (strncmp(np, nvpair_name(nvp), n) ||
1716185029Spjd			    (strlen(nvpair_name(nvp)) != n))
1717185029Spjd				continue;
1718185029Spjd
1719185029Spjd			/* if indexed, verify type is array oriented */
1720185029Spjd			if (idxp && !nvpair_type_is_array(nvp))
1721185029Spjd				goto fail;
1722185029Spjd
1723185029Spjd			/*
1724185029Spjd			 * Full match found, return nvp and idx if this
1725185029Spjd			 * was the last component.
1726185029Spjd			 */
1727185029Spjd			if (sepp == NULL) {
1728185029Spjd				if (ret)
1729185029Spjd					*ret = nvp;
1730185029Spjd				if (ip && idxp)
1731185029Spjd					*ip = (int)idx;	/* return index */
1732185029Spjd				return (0);		/* found */
1733185029Spjd			}
1734185029Spjd
1735185029Spjd			/*
1736185029Spjd			 * More components: current match must be
1737185029Spjd			 * of DATA_TYPE_NVLIST or DATA_TYPE_NVLIST_ARRAY
1738185029Spjd			 * to support going deeper.
1739185029Spjd			 */
1740185029Spjd			if (nvpair_type(nvp) == DATA_TYPE_NVLIST) {
1741185029Spjd				nvl = EMBEDDED_NVL(nvp);
1742185029Spjd				break;
1743185029Spjd			} else if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY) {
1744185029Spjd				(void) nvpair_value_nvlist_array(nvp,
1745185029Spjd				    &nva, (uint_t *)&n);
1746185029Spjd				if ((n < 0) || (idx >= n))
1747185029Spjd					goto fail;
1748185029Spjd				nvl = nva[idx];
1749185029Spjd				break;
1750185029Spjd			}
1751185029Spjd
1752185029Spjd			/* type does not support more levels */
1753185029Spjd			goto fail;
1754185029Spjd		}
1755185029Spjd		if (nvp == NULL)
1756185029Spjd			goto fail;		/* 'name' not found */
1757185029Spjd
1758185029Spjd		/* search for match of next component in embedded 'nvl' list */
1759185029Spjd	}
1760185029Spjd
1761185029Spjdfail:	if (ep && sepp)
1762185029Spjd		*ep = sepp;
1763185029Spjd	return (EINVAL);
1764185029Spjd}
1765185029Spjd
1766185029Spjd/*
1767185029Spjd * Return pointer to nvpair with specified 'name'.
1768185029Spjd */
1769168404Spjdint
1770185029Spjdnvlist_lookup_nvpair(nvlist_t *nvl, const char *name, nvpair_t **ret)
1771185029Spjd{
1772185029Spjd	return (nvlist_lookup_nvpair_ei_sep(nvl, name, 0, ret, NULL, NULL));
1773185029Spjd}
1774185029Spjd
1775185029Spjd/*
1776185029Spjd * Determine if named nvpair exists in nvlist (use embedded separator of '.'
1777185029Spjd * and return array index).  See nvlist_lookup_nvpair_ei_sep for more detailed
1778185029Spjd * description.
1779185029Spjd */
1780185029Spjdint nvlist_lookup_nvpair_embedded_index(nvlist_t *nvl,
1781185029Spjd    const char *name, nvpair_t **ret, int *ip, char **ep)
1782185029Spjd{
1783185029Spjd	return (nvlist_lookup_nvpair_ei_sep(nvl, name, '.', ret, ip, ep));
1784185029Spjd}
1785185029Spjd
1786185029Spjdboolean_t
1787185029Spjdnvlist_exists(nvlist_t *nvl, const char *name)
1788185029Spjd{
1789185029Spjd	nvpriv_t *priv;
1790185029Spjd	nvpair_t *nvp;
1791185029Spjd	i_nvp_t *curr;
1792185029Spjd
1793185029Spjd	if (name == NULL || nvl == NULL ||
1794185029Spjd	    (priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
1795185029Spjd		return (B_FALSE);
1796185029Spjd
1797185029Spjd	for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
1798185029Spjd		nvp = &curr->nvi_nvp;
1799185029Spjd
1800185029Spjd		if (strcmp(name, NVP_NAME(nvp)) == 0)
1801185029Spjd			return (B_TRUE);
1802185029Spjd	}
1803185029Spjd
1804185029Spjd	return (B_FALSE);
1805185029Spjd}
1806185029Spjd
1807185029Spjdint
1808168404Spjdnvpair_value_boolean_value(nvpair_t *nvp, boolean_t *val)
1809168404Spjd{
1810168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_VALUE, NULL, val));
1811168404Spjd}
1812168404Spjd
1813168404Spjdint
1814168404Spjdnvpair_value_byte(nvpair_t *nvp, uchar_t *val)
1815168404Spjd{
1816168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_BYTE, NULL, val));
1817168404Spjd}
1818168404Spjd
1819168404Spjdint
1820168404Spjdnvpair_value_int8(nvpair_t *nvp, int8_t *val)
1821168404Spjd{
1822168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT8, NULL, val));
1823168404Spjd}
1824168404Spjd
1825168404Spjdint
1826168404Spjdnvpair_value_uint8(nvpair_t *nvp, uint8_t *val)
1827168404Spjd{
1828168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT8, NULL, val));
1829168404Spjd}
1830168404Spjd
1831168404Spjdint
1832168404Spjdnvpair_value_int16(nvpair_t *nvp, int16_t *val)
1833168404Spjd{
1834168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT16, NULL, val));
1835168404Spjd}
1836168404Spjd
1837168404Spjdint
1838168404Spjdnvpair_value_uint16(nvpair_t *nvp, uint16_t *val)
1839168404Spjd{
1840168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT16, NULL, val));
1841168404Spjd}
1842168404Spjd
1843168404Spjdint
1844168404Spjdnvpair_value_int32(nvpair_t *nvp, int32_t *val)
1845168404Spjd{
1846168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT32, NULL, val));
1847168404Spjd}
1848168404Spjd
1849168404Spjdint
1850168404Spjdnvpair_value_uint32(nvpair_t *nvp, uint32_t *val)
1851168404Spjd{
1852168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT32, NULL, val));
1853168404Spjd}
1854168404Spjd
1855168404Spjdint
1856168404Spjdnvpair_value_int64(nvpair_t *nvp, int64_t *val)
1857168404Spjd{
1858168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT64, NULL, val));
1859168404Spjd}
1860168404Spjd
1861168404Spjdint
1862168404Spjdnvpair_value_uint64(nvpair_t *nvp, uint64_t *val)
1863168404Spjd{
1864168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT64, NULL, val));
1865168404Spjd}
1866168404Spjd
1867185029Spjd#if !defined(_KERNEL)
1868168404Spjdint
1869185029Spjdnvpair_value_double(nvpair_t *nvp, double *val)
1870185029Spjd{
1871185029Spjd	return (nvpair_value_common(nvp, DATA_TYPE_DOUBLE, NULL, val));
1872185029Spjd}
1873185029Spjd#endif
1874185029Spjd
1875185029Spjdint
1876168404Spjdnvpair_value_string(nvpair_t *nvp, char **val)
1877168404Spjd{
1878168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_STRING, NULL, val));
1879168404Spjd}
1880168404Spjd
1881168404Spjdint
1882168404Spjdnvpair_value_nvlist(nvpair_t *nvp, nvlist_t **val)
1883168404Spjd{
1884168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_NVLIST, NULL, val));
1885168404Spjd}
1886168404Spjd
1887168404Spjdint
1888168404Spjdnvpair_value_boolean_array(nvpair_t *nvp, boolean_t **val, uint_t *nelem)
1889168404Spjd{
1890168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_BOOLEAN_ARRAY, nelem, val));
1891168404Spjd}
1892168404Spjd
1893168404Spjdint
1894168404Spjdnvpair_value_byte_array(nvpair_t *nvp, uchar_t **val, uint_t *nelem)
1895168404Spjd{
1896168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_BYTE_ARRAY, nelem, val));
1897168404Spjd}
1898168404Spjd
1899168404Spjdint
1900168404Spjdnvpair_value_int8_array(nvpair_t *nvp, int8_t **val, uint_t *nelem)
1901168404Spjd{
1902168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT8_ARRAY, nelem, val));
1903168404Spjd}
1904168404Spjd
1905168404Spjdint
1906168404Spjdnvpair_value_uint8_array(nvpair_t *nvp, uint8_t **val, uint_t *nelem)
1907168404Spjd{
1908168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT8_ARRAY, nelem, val));
1909168404Spjd}
1910168404Spjd
1911168404Spjdint
1912168404Spjdnvpair_value_int16_array(nvpair_t *nvp, int16_t **val, uint_t *nelem)
1913168404Spjd{
1914168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT16_ARRAY, nelem, val));
1915168404Spjd}
1916168404Spjd
1917168404Spjdint
1918168404Spjdnvpair_value_uint16_array(nvpair_t *nvp, uint16_t **val, uint_t *nelem)
1919168404Spjd{
1920168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT16_ARRAY, nelem, val));
1921168404Spjd}
1922168404Spjd
1923168404Spjdint
1924168404Spjdnvpair_value_int32_array(nvpair_t *nvp, int32_t **val, uint_t *nelem)
1925168404Spjd{
1926168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT32_ARRAY, nelem, val));
1927168404Spjd}
1928168404Spjd
1929168404Spjdint
1930168404Spjdnvpair_value_uint32_array(nvpair_t *nvp, uint32_t **val, uint_t *nelem)
1931168404Spjd{
1932168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT32_ARRAY, nelem, val));
1933168404Spjd}
1934168404Spjd
1935168404Spjdint
1936168404Spjdnvpair_value_int64_array(nvpair_t *nvp, int64_t **val, uint_t *nelem)
1937168404Spjd{
1938168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_INT64_ARRAY, nelem, val));
1939168404Spjd}
1940168404Spjd
1941168404Spjdint
1942168404Spjdnvpair_value_uint64_array(nvpair_t *nvp, uint64_t **val, uint_t *nelem)
1943168404Spjd{
1944168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_UINT64_ARRAY, nelem, val));
1945168404Spjd}
1946168404Spjd
1947168404Spjdint
1948168404Spjdnvpair_value_string_array(nvpair_t *nvp, char ***val, uint_t *nelem)
1949168404Spjd{
1950168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_STRING_ARRAY, nelem, val));
1951168404Spjd}
1952168404Spjd
1953168404Spjdint
1954168404Spjdnvpair_value_nvlist_array(nvpair_t *nvp, nvlist_t ***val, uint_t *nelem)
1955168404Spjd{
1956168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_NVLIST_ARRAY, nelem, val));
1957168404Spjd}
1958168404Spjd
1959168404Spjdint
1960168404Spjdnvpair_value_hrtime(nvpair_t *nvp, hrtime_t *val)
1961168404Spjd{
1962168404Spjd	return (nvpair_value_common(nvp, DATA_TYPE_HRTIME, NULL, val));
1963168404Spjd}
1964168404Spjd
1965168404Spjd/*
1966168404Spjd * Add specified pair to the list.
1967168404Spjd */
1968168404Spjdint
1969168404Spjdnvlist_add_nvpair(nvlist_t *nvl, nvpair_t *nvp)
1970168404Spjd{
1971168404Spjd	if (nvl == NULL || nvp == NULL)
1972168404Spjd		return (EINVAL);
1973168404Spjd
1974168404Spjd	return (nvlist_add_common(nvl, NVP_NAME(nvp), NVP_TYPE(nvp),
1975168404Spjd	    NVP_NELEM(nvp), NVP_VALUE(nvp)));
1976168404Spjd}
1977168404Spjd
1978168404Spjd/*
1979168404Spjd * Merge the supplied nvlists and put the result in dst.
1980168404Spjd * The merged list will contain all names specified in both lists,
1981168404Spjd * the values are taken from nvl in the case of duplicates.
1982168404Spjd * Return 0 on success.
1983168404Spjd */
1984168404Spjd/*ARGSUSED*/
1985168404Spjdint
1986168404Spjdnvlist_merge(nvlist_t *dst, nvlist_t *nvl, int flag)
1987168404Spjd{
1988168404Spjd	if (nvl == NULL || dst == NULL)
1989168404Spjd		return (EINVAL);
1990168404Spjd
1991168404Spjd	if (dst != nvl)
1992168404Spjd		return (nvlist_copy_pairs(nvl, dst));
1993168404Spjd
1994168404Spjd	return (0);
1995168404Spjd}
1996168404Spjd
1997168404Spjd/*
1998168404Spjd * Encoding related routines
1999168404Spjd */
2000168404Spjd#define	NVS_OP_ENCODE	0
2001168404Spjd#define	NVS_OP_DECODE	1
2002168404Spjd#define	NVS_OP_GETSIZE	2
2003168404Spjd
2004168404Spjdtypedef struct nvs_ops nvs_ops_t;
2005168404Spjd
2006168404Spjdtypedef struct {
2007168404Spjd	int		nvs_op;
2008168404Spjd	const nvs_ops_t	*nvs_ops;
2009168404Spjd	void		*nvs_private;
2010168404Spjd	nvpriv_t	*nvs_priv;
2011168404Spjd} nvstream_t;
2012168404Spjd
2013168404Spjd/*
2014168404Spjd * nvs operations are:
2015168404Spjd *   - nvs_nvlist
2016168404Spjd *     encoding / decoding of a nvlist header (nvlist_t)
2017168404Spjd *     calculates the size used for header and end detection
2018168404Spjd *
2019168404Spjd *   - nvs_nvpair
2020168404Spjd *     responsible for the first part of encoding / decoding of an nvpair
2021168404Spjd *     calculates the decoded size of an nvpair
2022168404Spjd *
2023168404Spjd *   - nvs_nvp_op
2024168404Spjd *     second part of encoding / decoding of an nvpair
2025168404Spjd *
2026168404Spjd *   - nvs_nvp_size
2027168404Spjd *     calculates the encoding size of an nvpair
2028168404Spjd *
2029168404Spjd *   - nvs_nvl_fini
2030168404Spjd *     encodes the end detection mark (zeros).
2031168404Spjd */
2032168404Spjdstruct nvs_ops {
2033168404Spjd	int (*nvs_nvlist)(nvstream_t *, nvlist_t *, size_t *);
2034168404Spjd	int (*nvs_nvpair)(nvstream_t *, nvpair_t *, size_t *);
2035168404Spjd	int (*nvs_nvp_op)(nvstream_t *, nvpair_t *);
2036168404Spjd	int (*nvs_nvp_size)(nvstream_t *, nvpair_t *, size_t *);
2037168404Spjd	int (*nvs_nvl_fini)(nvstream_t *);
2038168404Spjd};
2039168404Spjd
2040168404Spjdtypedef struct {
2041168404Spjd	char	nvh_encoding;	/* nvs encoding method */
2042168404Spjd	char	nvh_endian;	/* nvs endian */
2043168404Spjd	char	nvh_reserved1;	/* reserved for future use */
2044168404Spjd	char	nvh_reserved2;	/* reserved for future use */
2045168404Spjd} nvs_header_t;
2046168404Spjd
2047168404Spjdstatic int
2048168404Spjdnvs_encode_pairs(nvstream_t *nvs, nvlist_t *nvl)
2049168404Spjd{
2050168404Spjd	nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv;
2051168404Spjd	i_nvp_t *curr;
2052168404Spjd
2053168404Spjd	/*
2054168404Spjd	 * Walk nvpair in list and encode each nvpair
2055168404Spjd	 */
2056168404Spjd	for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next)
2057168404Spjd		if (nvs->nvs_ops->nvs_nvpair(nvs, &curr->nvi_nvp, NULL) != 0)
2058168404Spjd			return (EFAULT);
2059168404Spjd
2060168404Spjd	return (nvs->nvs_ops->nvs_nvl_fini(nvs));
2061168404Spjd}
2062168404Spjd
2063168404Spjdstatic int
2064168404Spjdnvs_decode_pairs(nvstream_t *nvs, nvlist_t *nvl)
2065168404Spjd{
2066168404Spjd	nvpair_t *nvp;
2067168404Spjd	size_t nvsize;
2068168404Spjd	int err;
2069168404Spjd
2070168404Spjd	/*
2071168404Spjd	 * Get decoded size of next pair in stream, alloc
2072168404Spjd	 * memory for nvpair_t, then decode the nvpair
2073168404Spjd	 */
2074168404Spjd	while ((err = nvs->nvs_ops->nvs_nvpair(nvs, NULL, &nvsize)) == 0) {
2075168404Spjd		if (nvsize == 0) /* end of list */
2076168404Spjd			break;
2077168404Spjd
2078168404Spjd		/* make sure len makes sense */
2079168404Spjd		if (nvsize < NVP_SIZE_CALC(1, 0))
2080168404Spjd			return (EFAULT);
2081168404Spjd
2082168404Spjd		if ((nvp = nvp_buf_alloc(nvl, nvsize)) == NULL)
2083168404Spjd			return (ENOMEM);
2084168404Spjd
2085168404Spjd		if ((err = nvs->nvs_ops->nvs_nvp_op(nvs, nvp)) != 0) {
2086168404Spjd			nvp_buf_free(nvl, nvp);
2087168404Spjd			return (err);
2088168404Spjd		}
2089168404Spjd
2090168404Spjd		if (i_validate_nvpair(nvp) != 0) {
2091168404Spjd			nvpair_free(nvp);
2092168404Spjd			nvp_buf_free(nvl, nvp);
2093168404Spjd			return (EFAULT);
2094168404Spjd		}
2095168404Spjd
2096168404Spjd		nvp_buf_link(nvl, nvp);
2097168404Spjd	}
2098168404Spjd	return (err);
2099168404Spjd}
2100168404Spjd
2101168404Spjdstatic int
2102168404Spjdnvs_getsize_pairs(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen)
2103168404Spjd{
2104168404Spjd	nvpriv_t *priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv;
2105168404Spjd	i_nvp_t *curr;
2106168404Spjd	uint64_t nvsize = *buflen;
2107168404Spjd	size_t size;
2108168404Spjd
2109168404Spjd	/*
2110168404Spjd	 * Get encoded size of nvpairs in nvlist
2111168404Spjd	 */
2112168404Spjd	for (curr = priv->nvp_list; curr != NULL; curr = curr->nvi_next) {
2113168404Spjd		if (nvs->nvs_ops->nvs_nvp_size(nvs, &curr->nvi_nvp, &size) != 0)
2114168404Spjd			return (EINVAL);
2115168404Spjd
2116168404Spjd		if ((nvsize += size) > INT32_MAX)
2117168404Spjd			return (EINVAL);
2118168404Spjd	}
2119168404Spjd
2120168404Spjd	*buflen = nvsize;
2121168404Spjd	return (0);
2122168404Spjd}
2123168404Spjd
2124168404Spjdstatic int
2125168404Spjdnvs_operation(nvstream_t *nvs, nvlist_t *nvl, size_t *buflen)
2126168404Spjd{
2127168404Spjd	int err;
2128168404Spjd
2129168404Spjd	if (nvl->nvl_priv == 0)
2130168404Spjd		return (EFAULT);
2131168404Spjd
2132168404Spjd	/*
2133168404Spjd	 * Perform the operation, starting with header, then each nvpair
2134168404Spjd	 */
2135168404Spjd	if ((err = nvs->nvs_ops->nvs_nvlist(nvs, nvl, buflen)) != 0)
2136168404Spjd		return (err);
2137168404Spjd
2138168404Spjd	switch (nvs->nvs_op) {
2139168404Spjd	case NVS_OP_ENCODE:
2140168404Spjd		err = nvs_encode_pairs(nvs, nvl);
2141168404Spjd		break;
2142168404Spjd
2143168404Spjd	case NVS_OP_DECODE:
2144168404Spjd		err = nvs_decode_pairs(nvs, nvl);
2145168404Spjd		break;
2146168404Spjd
2147168404Spjd	case NVS_OP_GETSIZE:
2148168404Spjd		err = nvs_getsize_pairs(nvs, nvl, buflen);
2149168404Spjd		break;
2150168404Spjd
2151168404Spjd	default:
2152168404Spjd		err = EINVAL;
2153168404Spjd	}
2154168404Spjd
2155168404Spjd	return (err);
2156168404Spjd}
2157168404Spjd
2158168404Spjdstatic int
2159168404Spjdnvs_embedded(nvstream_t *nvs, nvlist_t *embedded)
2160168404Spjd{
2161168404Spjd	switch (nvs->nvs_op) {
2162168404Spjd	case NVS_OP_ENCODE:
2163168404Spjd		return (nvs_operation(nvs, embedded, NULL));
2164168404Spjd
2165168404Spjd	case NVS_OP_DECODE: {
2166168404Spjd		nvpriv_t *priv;
2167168404Spjd		int err;
2168168404Spjd
2169168404Spjd		if (embedded->nvl_version != NV_VERSION)
2170168404Spjd			return (ENOTSUP);
2171168404Spjd
2172168404Spjd		if ((priv = nv_priv_alloc_embedded(nvs->nvs_priv)) == NULL)
2173168404Spjd			return (ENOMEM);
2174168404Spjd
2175168404Spjd		nvlist_init(embedded, embedded->nvl_nvflag, priv);
2176168404Spjd
2177168404Spjd		if ((err = nvs_operation(nvs, embedded, NULL)) != 0)
2178168404Spjd			nvlist_free(embedded);
2179168404Spjd		return (err);
2180168404Spjd	}
2181168404Spjd	default:
2182168404Spjd		break;
2183168404Spjd	}
2184168404Spjd
2185168404Spjd	return (EINVAL);
2186168404Spjd}
2187168404Spjd
2188168404Spjdstatic int
2189168404Spjdnvs_embedded_nvl_array(nvstream_t *nvs, nvpair_t *nvp, size_t *size)
2190168404Spjd{
2191168404Spjd	size_t nelem = NVP_NELEM(nvp);
2192168404Spjd	nvlist_t **nvlp = EMBEDDED_NVL_ARRAY(nvp);
2193168404Spjd	int i;
2194168404Spjd
2195168404Spjd	switch (nvs->nvs_op) {
2196168404Spjd	case NVS_OP_ENCODE:
2197168404Spjd		for (i = 0; i < nelem; i++)
2198168404Spjd			if (nvs_embedded(nvs, nvlp[i]) != 0)
2199168404Spjd				return (EFAULT);
2200168404Spjd		break;
2201168404Spjd
2202168404Spjd	case NVS_OP_DECODE: {
2203168404Spjd		size_t len = nelem * sizeof (uint64_t);
2204168404Spjd		nvlist_t *embedded = (nvlist_t *)((uintptr_t)nvlp + len);
2205168404Spjd
2206168404Spjd		bzero(nvlp, len);	/* don't trust packed data */
2207168404Spjd		for (i = 0; i < nelem; i++) {
2208168404Spjd			if (nvs_embedded(nvs, embedded) != 0) {
2209168404Spjd				nvpair_free(nvp);
2210168404Spjd				return (EFAULT);
2211168404Spjd			}
2212168404Spjd
2213168404Spjd			nvlp[i] = embedded++;
2214168404Spjd		}
2215168404Spjd		break;
2216168404Spjd	}
2217168404Spjd	case NVS_OP_GETSIZE: {
2218168404Spjd		uint64_t nvsize = 0;
2219168404Spjd
2220168404Spjd		for (i = 0; i < nelem; i++) {
2221168404Spjd			size_t nvp_sz = 0;
2222168404Spjd
2223168404Spjd			if (nvs_operation(nvs, nvlp[i], &nvp_sz) != 0)
2224168404Spjd				return (EINVAL);
2225168404Spjd
2226168404Spjd			if ((nvsize += nvp_sz) > INT32_MAX)
2227168404Spjd				return (EINVAL);
2228168404Spjd		}
2229168404Spjd
2230168404Spjd		*size = nvsize;
2231168404Spjd		break;
2232168404Spjd	}
2233168404Spjd	default:
2234168404Spjd		return (EINVAL);
2235168404Spjd	}
2236168404Spjd
2237168404Spjd	return (0);
2238168404Spjd}
2239168404Spjd
2240168404Spjdstatic int nvs_native(nvstream_t *, nvlist_t *, char *, size_t *);
2241168404Spjdstatic int nvs_xdr(nvstream_t *, nvlist_t *, char *, size_t *);
2242168404Spjd
2243168404Spjd/*
2244168404Spjd * Common routine for nvlist operations:
2245168404Spjd * encode, decode, getsize (encoded size).
2246168404Spjd */
2247168404Spjdstatic int
2248168404Spjdnvlist_common(nvlist_t *nvl, char *buf, size_t *buflen, int encoding,
2249168404Spjd    int nvs_op)
2250168404Spjd{
2251168404Spjd	int err = 0;
2252168404Spjd	nvstream_t nvs;
2253168404Spjd	int nvl_endian;
2254174047Sjb#if BYTE_ORDER == _LITTLE_ENDIAN
2255168404Spjd	int host_endian = 1;
2256168404Spjd#else
2257168404Spjd	int host_endian = 0;
2258168404Spjd#endif	/* _LITTLE_ENDIAN */
2259168404Spjd	nvs_header_t *nvh = (void *)buf;
2260168404Spjd
2261168404Spjd	if (buflen == NULL || nvl == NULL ||
2262168404Spjd	    (nvs.nvs_priv = (nvpriv_t *)(uintptr_t)nvl->nvl_priv) == NULL)
2263168404Spjd		return (EINVAL);
2264168404Spjd
2265168404Spjd	nvs.nvs_op = nvs_op;
2266168404Spjd
2267168404Spjd	/*
2268168404Spjd	 * For NVS_OP_ENCODE and NVS_OP_DECODE make sure an nvlist and
2269168404Spjd	 * a buffer is allocated.  The first 4 bytes in the buffer are
2270168404Spjd	 * used for encoding method and host endian.
2271168404Spjd	 */
2272168404Spjd	switch (nvs_op) {
2273168404Spjd	case NVS_OP_ENCODE:
2274168404Spjd		if (buf == NULL || *buflen < sizeof (nvs_header_t))
2275168404Spjd			return (EINVAL);
2276168404Spjd
2277168404Spjd		nvh->nvh_encoding = encoding;
2278168404Spjd		nvh->nvh_endian = nvl_endian = host_endian;
2279168404Spjd		nvh->nvh_reserved1 = 0;
2280168404Spjd		nvh->nvh_reserved2 = 0;
2281168404Spjd		break;
2282168404Spjd
2283168404Spjd	case NVS_OP_DECODE:
2284168404Spjd		if (buf == NULL || *buflen < sizeof (nvs_header_t))
2285168404Spjd			return (EINVAL);
2286168404Spjd
2287168404Spjd		/* get method of encoding from first byte */
2288168404Spjd		encoding = nvh->nvh_encoding;
2289168404Spjd		nvl_endian = nvh->nvh_endian;
2290168404Spjd		break;
2291168404Spjd
2292168404Spjd	case NVS_OP_GETSIZE:
2293168404Spjd		nvl_endian = host_endian;
2294168404Spjd
2295168404Spjd		/*
2296168404Spjd		 * add the size for encoding
2297168404Spjd		 */
2298168404Spjd		*buflen = sizeof (nvs_header_t);
2299168404Spjd		break;
2300168404Spjd
2301168404Spjd	default:
2302168404Spjd		return (ENOTSUP);
2303168404Spjd	}
2304168404Spjd
2305168404Spjd	/*
2306168404Spjd	 * Create an nvstream with proper encoding method
2307168404Spjd	 */
2308168404Spjd	switch (encoding) {
2309168404Spjd	case NV_ENCODE_NATIVE:
2310168404Spjd		/*
2311168404Spjd		 * check endianness, in case we are unpacking
2312168404Spjd		 * from a file
2313168404Spjd		 */
2314168404Spjd		if (nvl_endian != host_endian)
2315168404Spjd			return (ENOTSUP);
2316168404Spjd		err = nvs_native(&nvs, nvl, buf, buflen);
2317168404Spjd		break;
2318168404Spjd	case NV_ENCODE_XDR:
2319168404Spjd		err = nvs_xdr(&nvs, nvl, buf, buflen);
2320168404Spjd		break;
2321168404Spjd	default:
2322168404Spjd		err = ENOTSUP;
2323168404Spjd		break;
2324168404Spjd	}
2325168404Spjd
2326168404Spjd	return (err);
2327168404Spjd}
2328168404Spjd
2329168404Spjdint
2330168404Spjdnvlist_size(nvlist_t *nvl, size_t *size, int encoding)
2331168404Spjd{
2332168404Spjd	return (nvlist_common(nvl, NULL, size, encoding, NVS_OP_GETSIZE));
2333168404Spjd}
2334168404Spjd
2335168404Spjd/*
2336168404Spjd * Pack nvlist into contiguous memory
2337168404Spjd */
2338168404Spjd/*ARGSUSED1*/
2339168404Spjdint
2340168404Spjdnvlist_pack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding,
2341168404Spjd    int kmflag)
2342168404Spjd{
2343168404Spjd#if defined(_KERNEL) && !defined(_BOOT)
2344168404Spjd	return (nvlist_xpack(nvl, bufp, buflen, encoding,
2345168404Spjd	    (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep)));
2346168404Spjd#else
2347168404Spjd	return (nvlist_xpack(nvl, bufp, buflen, encoding, nv_alloc_nosleep));
2348168404Spjd#endif
2349168404Spjd}
2350168404Spjd
2351168404Spjdint
2352168404Spjdnvlist_xpack(nvlist_t *nvl, char **bufp, size_t *buflen, int encoding,
2353168404Spjd    nv_alloc_t *nva)
2354168404Spjd{
2355168404Spjd	nvpriv_t nvpriv;
2356168404Spjd	size_t alloc_size;
2357168404Spjd	char *buf;
2358168404Spjd	int err;
2359168404Spjd
2360168404Spjd	if (nva == NULL || nvl == NULL || bufp == NULL || buflen == NULL)
2361168404Spjd		return (EINVAL);
2362168404Spjd
2363168404Spjd	if (*bufp != NULL)
2364168404Spjd		return (nvlist_common(nvl, *bufp, buflen, encoding,
2365168404Spjd		    NVS_OP_ENCODE));
2366168404Spjd
2367168404Spjd	/*
2368168404Spjd	 * Here is a difficult situation:
2369168404Spjd	 * 1. The nvlist has fixed allocator properties.
2370168404Spjd	 *    All other nvlist routines (like nvlist_add_*, ...) use
2371168404Spjd	 *    these properties.
2372168404Spjd	 * 2. When using nvlist_pack() the user can specify his own
2373168404Spjd	 *    allocator properties (e.g. by using KM_NOSLEEP).
2374168404Spjd	 *
2375168404Spjd	 * We use the user specified properties (2). A clearer solution
2376168404Spjd	 * will be to remove the kmflag from nvlist_pack(), but we will
2377168404Spjd	 * not change the interface.
2378168404Spjd	 */
2379168404Spjd	nv_priv_init(&nvpriv, nva, 0);
2380168404Spjd
2381168404Spjd	if (err = nvlist_size(nvl, &alloc_size, encoding))
2382168404Spjd		return (err);
2383168404Spjd
2384168404Spjd	if ((buf = nv_mem_zalloc(&nvpriv, alloc_size)) == NULL)
2385168404Spjd		return (ENOMEM);
2386168404Spjd
2387168404Spjd	if ((err = nvlist_common(nvl, buf, &alloc_size, encoding,
2388168404Spjd	    NVS_OP_ENCODE)) != 0) {
2389168404Spjd		nv_mem_free(&nvpriv, buf, alloc_size);
2390168404Spjd	} else {
2391168404Spjd		*buflen = alloc_size;
2392168404Spjd		*bufp = buf;
2393168404Spjd	}
2394168404Spjd
2395168404Spjd	return (err);
2396168404Spjd}
2397168404Spjd
2398168404Spjd/*
2399168404Spjd * Unpack buf into an nvlist_t
2400168404Spjd */
2401168404Spjd/*ARGSUSED1*/
2402168404Spjdint
2403168404Spjdnvlist_unpack(char *buf, size_t buflen, nvlist_t **nvlp, int kmflag)
2404168404Spjd{
2405168404Spjd#if defined(_KERNEL) && !defined(_BOOT)
2406168404Spjd	return (nvlist_xunpack(buf, buflen, nvlp,
2407168404Spjd	    (kmflag == KM_SLEEP ? nv_alloc_sleep : nv_alloc_nosleep)));
2408168404Spjd#else
2409168404Spjd	return (nvlist_xunpack(buf, buflen, nvlp, nv_alloc_nosleep));
2410168404Spjd#endif
2411168404Spjd}
2412168404Spjd
2413168404Spjdint
2414168404Spjdnvlist_xunpack(char *buf, size_t buflen, nvlist_t **nvlp, nv_alloc_t *nva)
2415168404Spjd{
2416168404Spjd	nvlist_t *nvl;
2417168404Spjd	int err;
2418168404Spjd
2419168404Spjd	if (nvlp == NULL)
2420168404Spjd		return (EINVAL);
2421168404Spjd
2422168404Spjd	if ((err = nvlist_xalloc(&nvl, 0, nva)) != 0)
2423168404Spjd		return (err);
2424168404Spjd
2425168404Spjd	if ((err = nvlist_common(nvl, buf, &buflen, 0, NVS_OP_DECODE)) != 0)
2426168404Spjd		nvlist_free(nvl);
2427168404Spjd	else
2428168404Spjd		*nvlp = nvl;
2429168404Spjd
2430168404Spjd	return (err);
2431168404Spjd}
2432168404Spjd
2433168404Spjd/*
2434168404Spjd * Native encoding functions
2435168404Spjd */
2436168404Spjdtypedef struct {
2437168404Spjd	/*
2438168404Spjd	 * This structure is used when decoding a packed nvpair in
2439168404Spjd	 * the native format.  n_base points to a buffer containing the
2440168404Spjd	 * packed nvpair.  n_end is a pointer to the end of the buffer.
2441168404Spjd	 * (n_end actually points to the first byte past the end of the
2442168404Spjd	 * buffer.)  n_curr is a pointer that lies between n_base and n_end.
2443168404Spjd	 * It points to the current data that we are decoding.
2444168404Spjd	 * The amount of data left in the buffer is equal to n_end - n_curr.
2445168404Spjd	 * n_flag is used to recognize a packed embedded list.
2446168404Spjd	 */
2447168404Spjd	caddr_t n_base;
2448168404Spjd	caddr_t n_end;
2449168404Spjd	caddr_t n_curr;
2450168404Spjd	uint_t  n_flag;
2451168404Spjd} nvs_native_t;
2452168404Spjd
2453168404Spjdstatic int
2454168404Spjdnvs_native_create(nvstream_t *nvs, nvs_native_t *native, char *buf,
2455168404Spjd    size_t buflen)
2456168404Spjd{
2457168404Spjd	switch (nvs->nvs_op) {
2458168404Spjd	case NVS_OP_ENCODE:
2459168404Spjd	case NVS_OP_DECODE:
2460168404Spjd		nvs->nvs_private = native;
2461168404Spjd		native->n_curr = native->n_base = buf;
2462168404Spjd		native->n_end = buf + buflen;
2463168404Spjd		native->n_flag = 0;
2464168404Spjd		return (0);
2465168404Spjd
2466168404Spjd	case NVS_OP_GETSIZE:
2467168404Spjd		nvs->nvs_private = native;
2468168404Spjd		native->n_curr = native->n_base = native->n_end = NULL;
2469168404Spjd		native->n_flag = 0;
2470168404Spjd		return (0);
2471168404Spjd	default:
2472168404Spjd		return (EINVAL);
2473168404Spjd	}
2474168404Spjd}
2475168404Spjd
2476168404Spjd/*ARGSUSED*/
2477168404Spjdstatic void
2478168404Spjdnvs_native_destroy(nvstream_t *nvs)
2479168404Spjd{
2480168404Spjd}
2481168404Spjd
2482168404Spjdstatic int
2483168404Spjdnative_cp(nvstream_t *nvs, void *buf, size_t size)
2484168404Spjd{
2485168404Spjd	nvs_native_t *native = (nvs_native_t *)nvs->nvs_private;
2486168404Spjd
2487168404Spjd	if (native->n_curr + size > native->n_end)
2488168404Spjd		return (EFAULT);
2489168404Spjd
2490168404Spjd	/*
2491168404Spjd	 * The bcopy() below eliminates alignment requirement
2492168404Spjd	 * on the buffer (stream) and is preferred over direct access.
2493168404Spjd	 */
2494168404Spjd	switch (nvs->nvs_op) {
2495168404Spjd	case NVS_OP_ENCODE:
2496168404Spjd		bcopy(buf, native->n_curr, size);
2497168404Spjd		break;
2498168404Spjd	case NVS_OP_DECODE:
2499168404Spjd		bcopy(native->n_curr, buf, size);
2500168404Spjd		break;
2501168404Spjd	default:
2502168404Spjd		return (EINVAL);
2503168404Spjd	}
2504168404Spjd
2505168404Spjd	native->n_curr += size;
2506168404Spjd	return (0);
2507168404Spjd}
2508168404Spjd
2509168404Spjd/*
2510168404Spjd * operate on nvlist_t header
2511168404Spjd */
2512168404Spjdstatic int
2513168404Spjdnvs_native_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size)
2514168404Spjd{
2515168404Spjd	nvs_native_t *native = nvs->nvs_private;
2516168404Spjd
2517168404Spjd	switch (nvs->nvs_op) {
2518168404Spjd	case NVS_OP_ENCODE:
2519168404Spjd	case NVS_OP_DECODE:
2520168404Spjd		if (native->n_flag)
2521168404Spjd			return (0);	/* packed embedded list */
2522168404Spjd
2523168404Spjd		native->n_flag = 1;
2524168404Spjd
2525168404Spjd		/* copy version and nvflag of the nvlist_t */
2526168404Spjd		if (native_cp(nvs, &nvl->nvl_version, sizeof (int32_t)) != 0 ||
2527168404Spjd		    native_cp(nvs, &nvl->nvl_nvflag, sizeof (int32_t)) != 0)
2528168404Spjd			return (EFAULT);
2529168404Spjd
2530168404Spjd		return (0);
2531168404Spjd
2532168404Spjd	case NVS_OP_GETSIZE:
2533168404Spjd		/*
2534168404Spjd		 * if calculate for packed embedded list
2535168404Spjd		 * 	4 for end of the embedded list
2536168404Spjd		 * else
2537168404Spjd		 * 	2 * sizeof (int32_t) for nvl_version and nvl_nvflag
2538168404Spjd		 * 	and 4 for end of the entire list
2539168404Spjd		 */
2540168404Spjd		if (native->n_flag) {
2541168404Spjd			*size += 4;
2542168404Spjd		} else {
2543168404Spjd			native->n_flag = 1;
2544168404Spjd			*size += 2 * sizeof (int32_t) + 4;
2545168404Spjd		}
2546168404Spjd
2547168404Spjd		return (0);
2548168404Spjd
2549168404Spjd	default:
2550168404Spjd		return (EINVAL);
2551168404Spjd	}
2552168404Spjd}
2553168404Spjd
2554168404Spjdstatic int
2555168404Spjdnvs_native_nvl_fini(nvstream_t *nvs)
2556168404Spjd{
2557168404Spjd	if (nvs->nvs_op == NVS_OP_ENCODE) {
2558168404Spjd		nvs_native_t *native = (nvs_native_t *)nvs->nvs_private;
2559168404Spjd		/*
2560168404Spjd		 * Add 4 zero bytes at end of nvlist. They are used
2561168404Spjd		 * for end detection by the decode routine.
2562168404Spjd		 */
2563168404Spjd		if (native->n_curr + sizeof (int) > native->n_end)
2564168404Spjd			return (EFAULT);
2565168404Spjd
2566168404Spjd		bzero(native->n_curr, sizeof (int));
2567168404Spjd		native->n_curr += sizeof (int);
2568168404Spjd	}
2569168404Spjd
2570168404Spjd	return (0);
2571168404Spjd}
2572168404Spjd
2573168404Spjdstatic int
2574168404Spjdnvpair_native_embedded(nvstream_t *nvs, nvpair_t *nvp)
2575168404Spjd{
2576168404Spjd	if (nvs->nvs_op == NVS_OP_ENCODE) {
2577168404Spjd		nvs_native_t *native = (nvs_native_t *)nvs->nvs_private;
2578196269Smarcel		char *packed = (void *)
2579168404Spjd		    (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp));
2580168404Spjd		/*
2581168404Spjd		 * Null out the pointer that is meaningless in the packed
2582168404Spjd		 * structure. The address may not be aligned, so we have
2583168404Spjd		 * to use bzero.
2584168404Spjd		 */
2585196269Smarcel		bzero(packed + offsetof(nvlist_t, nvl_priv),
2586196269Smarcel		    sizeof(((nvlist_t *)NULL)->nvl_priv));
2587168404Spjd	}
2588168404Spjd
2589168404Spjd	return (nvs_embedded(nvs, EMBEDDED_NVL(nvp)));
2590168404Spjd}
2591168404Spjd
2592168404Spjdstatic int
2593168404Spjdnvpair_native_embedded_array(nvstream_t *nvs, nvpair_t *nvp)
2594168404Spjd{
2595168404Spjd	if (nvs->nvs_op == NVS_OP_ENCODE) {
2596168404Spjd		nvs_native_t *native = (nvs_native_t *)nvs->nvs_private;
2597168404Spjd		char *value = native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp);
2598168404Spjd		size_t len = NVP_NELEM(nvp) * sizeof (uint64_t);
2599168404Spjd		int i;
2600168404Spjd		/*
2601168404Spjd		 * Null out pointers that are meaningless in the packed
2602168404Spjd		 * structure. The addresses may not be aligned, so we have
2603168404Spjd		 * to use bzero.
2604168404Spjd		 */
2605168404Spjd		bzero(value, len);
2606168404Spjd
2607195627Smarcel		value += len;
2608195627Smarcel		for (i = 0; i < NVP_NELEM(nvp); i++) {
2609168404Spjd			/*
2610168404Spjd			 * Null out the pointer that is meaningless in the
2611168404Spjd			 * packed structure. The address may not be aligned,
2612168404Spjd			 * so we have to use bzero.
2613168404Spjd			 */
2614195627Smarcel			bzero(value + offsetof(nvlist_t, nvl_priv),
2615195627Smarcel			    sizeof(((nvlist_t *)NULL)->nvl_priv));
2616195627Smarcel			value += sizeof(nvlist_t);
2617195627Smarcel		}
2618168404Spjd	}
2619168404Spjd
2620168404Spjd	return (nvs_embedded_nvl_array(nvs, nvp, NULL));
2621168404Spjd}
2622168404Spjd
2623168404Spjdstatic void
2624168404Spjdnvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp)
2625168404Spjd{
2626168404Spjd	switch (nvs->nvs_op) {
2627168404Spjd	case NVS_OP_ENCODE: {
2628168404Spjd		nvs_native_t *native = (nvs_native_t *)nvs->nvs_private;
2629168404Spjd		uint64_t *strp = (void *)
2630168404Spjd		    (native->n_curr - nvp->nvp_size + NVP_VALOFF(nvp));
2631168404Spjd		/*
2632168404Spjd		 * Null out pointers that are meaningless in the packed
2633168404Spjd		 * structure. The addresses may not be aligned, so we have
2634168404Spjd		 * to use bzero.
2635168404Spjd		 */
2636168404Spjd		bzero(strp, NVP_NELEM(nvp) * sizeof (uint64_t));
2637168404Spjd		break;
2638168404Spjd	}
2639168404Spjd	case NVS_OP_DECODE: {
2640168404Spjd		char **strp = (void *)NVP_VALUE(nvp);
2641168404Spjd		char *buf = ((char *)strp + NVP_NELEM(nvp) * sizeof (uint64_t));
2642168404Spjd		int i;
2643168404Spjd
2644168404Spjd		for (i = 0; i < NVP_NELEM(nvp); i++) {
2645168404Spjd			strp[i] = buf;
2646168404Spjd			buf += strlen(buf) + 1;
2647168404Spjd		}
2648168404Spjd		break;
2649168404Spjd	}
2650168404Spjd	}
2651168404Spjd}
2652168404Spjd
2653168404Spjdstatic int
2654168404Spjdnvs_native_nvp_op(nvstream_t *nvs, nvpair_t *nvp)
2655168404Spjd{
2656168404Spjd	data_type_t type;
2657168404Spjd	int value_sz;
2658168404Spjd	int ret = 0;
2659168404Spjd
2660168404Spjd	/*
2661168404Spjd	 * We do the initial bcopy of the data before we look at
2662168404Spjd	 * the nvpair type, because when we're decoding, we won't
2663168404Spjd	 * have the correct values for the pair until we do the bcopy.
2664168404Spjd	 */
2665168404Spjd	switch (nvs->nvs_op) {
2666168404Spjd	case NVS_OP_ENCODE:
2667168404Spjd	case NVS_OP_DECODE:
2668168404Spjd		if (native_cp(nvs, nvp, nvp->nvp_size) != 0)
2669168404Spjd			return (EFAULT);
2670168404Spjd		break;
2671168404Spjd	default:
2672168404Spjd		return (EINVAL);
2673168404Spjd	}
2674168404Spjd
2675168404Spjd	/* verify nvp_name_sz, check the name string length */
2676168404Spjd	if (i_validate_nvpair_name(nvp) != 0)
2677168404Spjd		return (EFAULT);
2678168404Spjd
2679168404Spjd	type = NVP_TYPE(nvp);
2680168404Spjd
2681168404Spjd	/*
2682168404Spjd	 * Verify type and nelem and get the value size.
2683168404Spjd	 * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY
2684168404Spjd	 * is the size of the string(s) excluded.
2685168404Spjd	 */
2686168404Spjd	if ((value_sz = i_get_value_size(type, NULL, NVP_NELEM(nvp))) < 0)
2687168404Spjd		return (EFAULT);
2688168404Spjd
2689168404Spjd	if (NVP_SIZE_CALC(nvp->nvp_name_sz, value_sz) > nvp->nvp_size)
2690168404Spjd		return (EFAULT);
2691168404Spjd
2692168404Spjd	switch (type) {
2693168404Spjd	case DATA_TYPE_NVLIST:
2694168404Spjd		ret = nvpair_native_embedded(nvs, nvp);
2695168404Spjd		break;
2696168404Spjd	case DATA_TYPE_NVLIST_ARRAY:
2697168404Spjd		ret = nvpair_native_embedded_array(nvs, nvp);
2698168404Spjd		break;
2699168404Spjd	case DATA_TYPE_STRING_ARRAY:
2700168404Spjd		nvpair_native_string_array(nvs, nvp);
2701168404Spjd		break;
2702168404Spjd	default:
2703168404Spjd		break;
2704168404Spjd	}
2705168404Spjd
2706168404Spjd	return (ret);
2707168404Spjd}
2708168404Spjd
2709168404Spjdstatic int
2710168404Spjdnvs_native_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size)
2711168404Spjd{
2712168404Spjd	uint64_t nvp_sz = nvp->nvp_size;
2713168404Spjd
2714168404Spjd	switch (NVP_TYPE(nvp)) {
2715168404Spjd	case DATA_TYPE_NVLIST: {
2716168404Spjd		size_t nvsize = 0;
2717168404Spjd
2718168404Spjd		if (nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize) != 0)
2719168404Spjd			return (EINVAL);
2720168404Spjd
2721168404Spjd		nvp_sz += nvsize;
2722168404Spjd		break;
2723168404Spjd	}
2724168404Spjd	case DATA_TYPE_NVLIST_ARRAY: {
2725168404Spjd		size_t nvsize;
2726168404Spjd
2727168404Spjd		if (nvs_embedded_nvl_array(nvs, nvp, &nvsize) != 0)
2728168404Spjd			return (EINVAL);
2729168404Spjd
2730168404Spjd		nvp_sz += nvsize;
2731168404Spjd		break;
2732168404Spjd	}
2733168404Spjd	default:
2734168404Spjd		break;
2735168404Spjd	}
2736168404Spjd
2737168404Spjd	if (nvp_sz > INT32_MAX)
2738168404Spjd		return (EINVAL);
2739168404Spjd
2740168404Spjd	*size = nvp_sz;
2741168404Spjd
2742168404Spjd	return (0);
2743168404Spjd}
2744168404Spjd
2745168404Spjdstatic int
2746168404Spjdnvs_native_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size)
2747168404Spjd{
2748168404Spjd	switch (nvs->nvs_op) {
2749168404Spjd	case NVS_OP_ENCODE:
2750168404Spjd		return (nvs_native_nvp_op(nvs, nvp));
2751168404Spjd
2752168404Spjd	case NVS_OP_DECODE: {
2753168404Spjd		nvs_native_t *native = (nvs_native_t *)nvs->nvs_private;
2754168404Spjd		int32_t decode_len;
2755168404Spjd
2756168404Spjd		/* try to read the size value from the stream */
2757168404Spjd		if (native->n_curr + sizeof (int32_t) > native->n_end)
2758168404Spjd			return (EFAULT);
2759168404Spjd		bcopy(native->n_curr, &decode_len, sizeof (int32_t));
2760168404Spjd
2761168404Spjd		/* sanity check the size value */
2762168404Spjd		if (decode_len < 0 ||
2763168404Spjd		    decode_len > native->n_end - native->n_curr)
2764168404Spjd			return (EFAULT);
2765168404Spjd
2766168404Spjd		*size = decode_len;
2767168404Spjd
2768168404Spjd		/*
2769168404Spjd		 * If at the end of the stream then move the cursor
2770168404Spjd		 * forward, otherwise nvpair_native_op() will read
2771168404Spjd		 * the entire nvpair at the same cursor position.
2772168404Spjd		 */
2773168404Spjd		if (*size == 0)
2774168404Spjd			native->n_curr += sizeof (int32_t);
2775168404Spjd		break;
2776168404Spjd	}
2777168404Spjd
2778168404Spjd	default:
2779168404Spjd		return (EINVAL);
2780168404Spjd	}
2781168404Spjd
2782168404Spjd	return (0);
2783168404Spjd}
2784168404Spjd
2785168404Spjdstatic const nvs_ops_t nvs_native_ops = {
2786168404Spjd	nvs_native_nvlist,
2787168404Spjd	nvs_native_nvpair,
2788168404Spjd	nvs_native_nvp_op,
2789168404Spjd	nvs_native_nvp_size,
2790168404Spjd	nvs_native_nvl_fini
2791168404Spjd};
2792168404Spjd
2793168404Spjdstatic int
2794168404Spjdnvs_native(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen)
2795168404Spjd{
2796168404Spjd	nvs_native_t native;
2797168404Spjd	int err;
2798168404Spjd
2799168404Spjd	nvs->nvs_ops = &nvs_native_ops;
2800168404Spjd
2801168404Spjd	if ((err = nvs_native_create(nvs, &native, buf + sizeof (nvs_header_t),
2802168404Spjd	    *buflen - sizeof (nvs_header_t))) != 0)
2803168404Spjd		return (err);
2804168404Spjd
2805168404Spjd	err = nvs_operation(nvs, nvl, buflen);
2806168404Spjd
2807168404Spjd	nvs_native_destroy(nvs);
2808168404Spjd
2809168404Spjd	return (err);
2810168404Spjd}
2811168404Spjd
2812168404Spjd/*
2813168404Spjd * XDR encoding functions
2814168404Spjd *
2815168404Spjd * An xdr packed nvlist is encoded as:
2816168404Spjd *
2817168404Spjd *  - encoding methode and host endian (4 bytes)
2818168404Spjd *  - nvl_version (4 bytes)
2819168404Spjd *  - nvl_nvflag (4 bytes)
2820168404Spjd *
2821168404Spjd *  - encoded nvpairs, the format of one xdr encoded nvpair is:
2822168404Spjd *	- encoded size of the nvpair (4 bytes)
2823168404Spjd *	- decoded size of the nvpair (4 bytes)
2824168404Spjd *	- name string, (4 + sizeof(NV_ALIGN4(string))
2825168404Spjd *	  a string is coded as size (4 bytes) and data
2826168404Spjd *	- data type (4 bytes)
2827168404Spjd *	- number of elements in the nvpair (4 bytes)
2828168404Spjd *	- data
2829168404Spjd *
2830168404Spjd *  - 2 zero's for end of the entire list (8 bytes)
2831168404Spjd */
2832168404Spjdstatic int
2833168404Spjdnvs_xdr_create(nvstream_t *nvs, XDR *xdr, char *buf, size_t buflen)
2834168404Spjd{
2835168404Spjd	/* xdr data must be 4 byte aligned */
2836168404Spjd	if ((ulong_t)buf % 4 != 0)
2837168404Spjd		return (EFAULT);
2838168404Spjd
2839168404Spjd	switch (nvs->nvs_op) {
2840168404Spjd	case NVS_OP_ENCODE:
2841168404Spjd		xdrmem_create(xdr, buf, (uint_t)buflen, XDR_ENCODE);
2842168404Spjd		nvs->nvs_private = xdr;
2843168404Spjd		return (0);
2844168404Spjd	case NVS_OP_DECODE:
2845168404Spjd		xdrmem_create(xdr, buf, (uint_t)buflen, XDR_DECODE);
2846168404Spjd		nvs->nvs_private = xdr;
2847168404Spjd		return (0);
2848168404Spjd	case NVS_OP_GETSIZE:
2849168404Spjd		nvs->nvs_private = NULL;
2850168404Spjd		return (0);
2851168404Spjd	default:
2852168404Spjd		return (EINVAL);
2853168404Spjd	}
2854168404Spjd}
2855168404Spjd
2856168404Spjdstatic void
2857168404Spjdnvs_xdr_destroy(nvstream_t *nvs)
2858168404Spjd{
2859168404Spjd	switch (nvs->nvs_op) {
2860168404Spjd	case NVS_OP_ENCODE:
2861168404Spjd	case NVS_OP_DECODE:
2862168404Spjd		xdr_destroy((XDR *)nvs->nvs_private);
2863168404Spjd		break;
2864168404Spjd	default:
2865168404Spjd		break;
2866168404Spjd	}
2867168404Spjd}
2868168404Spjd
2869168404Spjdstatic int
2870168404Spjdnvs_xdr_nvlist(nvstream_t *nvs, nvlist_t *nvl, size_t *size)
2871168404Spjd{
2872168404Spjd	switch (nvs->nvs_op) {
2873168404Spjd	case NVS_OP_ENCODE:
2874168404Spjd	case NVS_OP_DECODE: {
2875168404Spjd		XDR 	*xdr = nvs->nvs_private;
2876168404Spjd
2877168404Spjd		if (!xdr_int(xdr, &nvl->nvl_version) ||
2878168404Spjd		    !xdr_u_int(xdr, &nvl->nvl_nvflag))
2879168404Spjd			return (EFAULT);
2880168404Spjd		break;
2881168404Spjd	}
2882168404Spjd	case NVS_OP_GETSIZE: {
2883168404Spjd		/*
2884168404Spjd		 * 2 * 4 for nvl_version + nvl_nvflag
2885168404Spjd		 * and 8 for end of the entire list
2886168404Spjd		 */
2887168404Spjd		*size += 2 * 4 + 8;
2888168404Spjd		break;
2889168404Spjd	}
2890168404Spjd	default:
2891168404Spjd		return (EINVAL);
2892168404Spjd	}
2893168404Spjd	return (0);
2894168404Spjd}
2895168404Spjd
2896168404Spjdstatic int
2897168404Spjdnvs_xdr_nvl_fini(nvstream_t *nvs)
2898168404Spjd{
2899168404Spjd	if (nvs->nvs_op == NVS_OP_ENCODE) {
2900168404Spjd		XDR *xdr = nvs->nvs_private;
2901168404Spjd		int zero = 0;
2902168404Spjd
2903168404Spjd		if (!xdr_int(xdr, &zero) || !xdr_int(xdr, &zero))
2904168404Spjd			return (EFAULT);
2905168404Spjd	}
2906168404Spjd
2907168404Spjd	return (0);
2908168404Spjd}
2909168404Spjd
2910168404Spjd/*
2911168404Spjd * The format of xdr encoded nvpair is:
2912168404Spjd * encode_size, decode_size, name string, data type, nelem, data
2913168404Spjd */
2914168404Spjdstatic int
2915168404Spjdnvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp)
2916168404Spjd{
2917168404Spjd	data_type_t type;
2918168404Spjd	char	*buf;
2919168404Spjd	char	*buf_end = (char *)nvp + nvp->nvp_size;
2920168404Spjd	int	value_sz;
2921168404Spjd	uint_t	nelem, buflen;
2922168404Spjd	bool_t	ret = FALSE;
2923168404Spjd	XDR	*xdr = nvs->nvs_private;
2924168404Spjd
2925168404Spjd	ASSERT(xdr != NULL && nvp != NULL);
2926168404Spjd
2927168404Spjd	/* name string */
2928168404Spjd	if ((buf = NVP_NAME(nvp)) >= buf_end)
2929168404Spjd		return (EFAULT);
2930168404Spjd	buflen = buf_end - buf;
2931168404Spjd
2932168404Spjd	if (!xdr_string(xdr, &buf, buflen - 1))
2933168404Spjd		return (EFAULT);
2934168404Spjd	nvp->nvp_name_sz = strlen(buf) + 1;
2935168404Spjd
2936168404Spjd	/* type and nelem */
2937168404Spjd	if (!xdr_int(xdr, (int *)&nvp->nvp_type) ||
2938168404Spjd	    !xdr_int(xdr, &nvp->nvp_value_elem))
2939168404Spjd		return (EFAULT);
2940168404Spjd
2941168404Spjd	type = NVP_TYPE(nvp);
2942168404Spjd	nelem = nvp->nvp_value_elem;
2943168404Spjd
2944168404Spjd	/*
2945168404Spjd	 * Verify type and nelem and get the value size.
2946168404Spjd	 * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY
2947168404Spjd	 * is the size of the string(s) excluded.
2948168404Spjd	 */
2949168404Spjd	if ((value_sz = i_get_value_size(type, NULL, nelem)) < 0)
2950168404Spjd		return (EFAULT);
2951168404Spjd
2952168404Spjd	/* if there is no data to extract then return */
2953168404Spjd	if (nelem == 0)
2954168404Spjd		return (0);
2955168404Spjd
2956168404Spjd	/* value */
2957168404Spjd	if ((buf = NVP_VALUE(nvp)) >= buf_end)
2958168404Spjd		return (EFAULT);
2959168404Spjd	buflen = buf_end - buf;
2960168404Spjd
2961168404Spjd	if (buflen < value_sz)
2962168404Spjd		return (EFAULT);
2963168404Spjd
2964168404Spjd	switch (type) {
2965168404Spjd	case DATA_TYPE_NVLIST:
2966168404Spjd		if (nvs_embedded(nvs, (void *)buf) == 0)
2967168404Spjd			return (0);
2968168404Spjd		break;
2969168404Spjd
2970168404Spjd	case DATA_TYPE_NVLIST_ARRAY:
2971168404Spjd		if (nvs_embedded_nvl_array(nvs, nvp, NULL) == 0)
2972168404Spjd			return (0);
2973168404Spjd		break;
2974168404Spjd
2975168404Spjd	case DATA_TYPE_BOOLEAN:
2976168404Spjd		ret = TRUE;
2977168404Spjd		break;
2978168404Spjd
2979168404Spjd	case DATA_TYPE_BYTE:
2980168404Spjd	case DATA_TYPE_INT8:
2981168404Spjd	case DATA_TYPE_UINT8:
2982168404Spjd		ret = xdr_char(xdr, buf);
2983168404Spjd		break;
2984168404Spjd
2985168404Spjd	case DATA_TYPE_INT16:
2986168404Spjd		ret = xdr_short(xdr, (void *)buf);
2987168404Spjd		break;
2988168404Spjd
2989168404Spjd	case DATA_TYPE_UINT16:
2990168404Spjd		ret = xdr_u_short(xdr, (void *)buf);
2991168404Spjd		break;
2992168404Spjd
2993168404Spjd	case DATA_TYPE_BOOLEAN_VALUE:
2994168404Spjd	case DATA_TYPE_INT32:
2995168404Spjd		ret = xdr_int(xdr, (void *)buf);
2996168404Spjd		break;
2997168404Spjd
2998168404Spjd	case DATA_TYPE_UINT32:
2999168404Spjd		ret = xdr_u_int(xdr, (void *)buf);
3000168404Spjd		break;
3001168404Spjd
3002168404Spjd	case DATA_TYPE_INT64:
3003168404Spjd		ret = xdr_longlong_t(xdr, (void *)buf);
3004168404Spjd		break;
3005168404Spjd
3006168404Spjd	case DATA_TYPE_UINT64:
3007168404Spjd		ret = xdr_u_longlong_t(xdr, (void *)buf);
3008168404Spjd		break;
3009168404Spjd
3010168404Spjd	case DATA_TYPE_HRTIME:
3011168404Spjd		/*
3012168404Spjd		 * NOTE: must expose the definition of hrtime_t here
3013168404Spjd		 */
3014168404Spjd		ret = xdr_longlong_t(xdr, (void *)buf);
3015168404Spjd		break;
3016185029Spjd#if !defined(_KERNEL)
3017185029Spjd	case DATA_TYPE_DOUBLE:
3018185029Spjd		ret = xdr_double(xdr, (void *)buf);
3019185029Spjd		break;
3020185029Spjd#endif
3021168404Spjd	case DATA_TYPE_STRING:
3022168404Spjd		ret = xdr_string(xdr, &buf, buflen - 1);
3023168404Spjd		break;
3024168404Spjd
3025168404Spjd	case DATA_TYPE_BYTE_ARRAY:
3026168404Spjd		ret = xdr_opaque(xdr, buf, nelem);
3027168404Spjd		break;
3028168404Spjd
3029168404Spjd	case DATA_TYPE_INT8_ARRAY:
3030168404Spjd	case DATA_TYPE_UINT8_ARRAY:
3031168404Spjd		ret = xdr_array(xdr, &buf, &nelem, buflen, sizeof (int8_t),
3032168404Spjd		    (xdrproc_t)xdr_char);
3033168404Spjd		break;
3034168404Spjd
3035168404Spjd	case DATA_TYPE_INT16_ARRAY:
3036168404Spjd		ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int16_t),
3037168404Spjd		    sizeof (int16_t), (xdrproc_t)xdr_short);
3038168404Spjd		break;
3039168404Spjd
3040168404Spjd	case DATA_TYPE_UINT16_ARRAY:
3041168404Spjd		ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint16_t),
3042168404Spjd		    sizeof (uint16_t), (xdrproc_t)xdr_u_short);
3043168404Spjd		break;
3044168404Spjd
3045168404Spjd	case DATA_TYPE_BOOLEAN_ARRAY:
3046168404Spjd	case DATA_TYPE_INT32_ARRAY:
3047168404Spjd		ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int32_t),
3048168404Spjd		    sizeof (int32_t), (xdrproc_t)xdr_int);
3049168404Spjd		break;
3050168404Spjd
3051168404Spjd	case DATA_TYPE_UINT32_ARRAY:
3052168404Spjd		ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint32_t),
3053168404Spjd		    sizeof (uint32_t), (xdrproc_t)xdr_u_int);
3054168404Spjd		break;
3055168404Spjd
3056168404Spjd	case DATA_TYPE_INT64_ARRAY:
3057168404Spjd		ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (int64_t),
3058168404Spjd		    sizeof (int64_t), (xdrproc_t)xdr_longlong_t);
3059168404Spjd		break;
3060168404Spjd
3061168404Spjd	case DATA_TYPE_UINT64_ARRAY:
3062168404Spjd		ret = xdr_array(xdr, &buf, &nelem, buflen / sizeof (uint64_t),
3063168404Spjd		    sizeof (uint64_t), (xdrproc_t)xdr_u_longlong_t);
3064168404Spjd		break;
3065168404Spjd
3066168404Spjd	case DATA_TYPE_STRING_ARRAY: {
3067168404Spjd		size_t len = nelem * sizeof (uint64_t);
3068168404Spjd		char **strp = (void *)buf;
3069168404Spjd		int i;
3070168404Spjd
3071168404Spjd		if (nvs->nvs_op == NVS_OP_DECODE)
3072168404Spjd			bzero(buf, len);	/* don't trust packed data */
3073168404Spjd
3074168404Spjd		for (i = 0; i < nelem; i++) {
3075168404Spjd			if (buflen <= len)
3076168404Spjd				return (EFAULT);
3077168404Spjd
3078168404Spjd			buf += len;
3079168404Spjd			buflen -= len;
3080168404Spjd
3081168404Spjd			if (xdr_string(xdr, &buf, buflen - 1) != TRUE)
3082168404Spjd				return (EFAULT);
3083168404Spjd
3084168404Spjd			if (nvs->nvs_op == NVS_OP_DECODE)
3085168404Spjd				strp[i] = buf;
3086168404Spjd			len = strlen(buf) + 1;
3087168404Spjd		}
3088168404Spjd		ret = TRUE;
3089168404Spjd		break;
3090168404Spjd	}
3091168404Spjd	default:
3092168404Spjd		break;
3093168404Spjd	}
3094168404Spjd
3095168404Spjd	return (ret == TRUE ? 0 : EFAULT);
3096168404Spjd}
3097168404Spjd
3098168404Spjdstatic int
3099168404Spjdnvs_xdr_nvp_size(nvstream_t *nvs, nvpair_t *nvp, size_t *size)
3100168404Spjd{
3101168404Spjd	data_type_t type = NVP_TYPE(nvp);
3102168404Spjd	/*
3103168404Spjd	 * encode_size + decode_size + name string size + data type + nelem
3104168404Spjd	 * where name string size = 4 + NV_ALIGN4(strlen(NVP_NAME(nvp)))
3105168404Spjd	 */
3106168404Spjd	uint64_t nvp_sz = 4 + 4 + 4 + NV_ALIGN4(strlen(NVP_NAME(nvp))) + 4 + 4;
3107168404Spjd
3108168404Spjd	switch (type) {
3109168404Spjd	case DATA_TYPE_BOOLEAN:
3110168404Spjd		break;
3111168404Spjd
3112168404Spjd	case DATA_TYPE_BOOLEAN_VALUE:
3113168404Spjd	case DATA_TYPE_BYTE:
3114168404Spjd	case DATA_TYPE_INT8:
3115168404Spjd	case DATA_TYPE_UINT8:
3116168404Spjd	case DATA_TYPE_INT16:
3117168404Spjd	case DATA_TYPE_UINT16:
3118168404Spjd	case DATA_TYPE_INT32:
3119168404Spjd	case DATA_TYPE_UINT32:
3120168404Spjd		nvp_sz += 4;	/* 4 is the minimum xdr unit */
3121168404Spjd		break;
3122168404Spjd
3123168404Spjd	case DATA_TYPE_INT64:
3124168404Spjd	case DATA_TYPE_UINT64:
3125168404Spjd	case DATA_TYPE_HRTIME:
3126185029Spjd#if !defined(_KERNEL)
3127185029Spjd	case DATA_TYPE_DOUBLE:
3128185029Spjd#endif
3129168404Spjd		nvp_sz += 8;
3130168404Spjd		break;
3131168404Spjd
3132168404Spjd	case DATA_TYPE_STRING:
3133168404Spjd		nvp_sz += 4 + NV_ALIGN4(strlen((char *)NVP_VALUE(nvp)));
3134168404Spjd		break;
3135168404Spjd
3136168404Spjd	case DATA_TYPE_BYTE_ARRAY:
3137168404Spjd		nvp_sz += NV_ALIGN4(NVP_NELEM(nvp));
3138168404Spjd		break;
3139168404Spjd
3140168404Spjd	case DATA_TYPE_BOOLEAN_ARRAY:
3141168404Spjd	case DATA_TYPE_INT8_ARRAY:
3142168404Spjd	case DATA_TYPE_UINT8_ARRAY:
3143168404Spjd	case DATA_TYPE_INT16_ARRAY:
3144168404Spjd	case DATA_TYPE_UINT16_ARRAY:
3145168404Spjd	case DATA_TYPE_INT32_ARRAY:
3146168404Spjd	case DATA_TYPE_UINT32_ARRAY:
3147168404Spjd		nvp_sz += 4 + 4 * (uint64_t)NVP_NELEM(nvp);
3148168404Spjd		break;
3149168404Spjd
3150168404Spjd	case DATA_TYPE_INT64_ARRAY:
3151168404Spjd	case DATA_TYPE_UINT64_ARRAY:
3152168404Spjd		nvp_sz += 4 + 8 * (uint64_t)NVP_NELEM(nvp);
3153168404Spjd		break;
3154168404Spjd
3155168404Spjd	case DATA_TYPE_STRING_ARRAY: {
3156168404Spjd		int i;
3157168404Spjd		char **strs = (void *)NVP_VALUE(nvp);
3158168404Spjd
3159168404Spjd		for (i = 0; i < NVP_NELEM(nvp); i++)
3160168404Spjd			nvp_sz += 4 + NV_ALIGN4(strlen(strs[i]));
3161168404Spjd
3162168404Spjd		break;
3163168404Spjd	}
3164168404Spjd
3165168404Spjd	case DATA_TYPE_NVLIST:
3166168404Spjd	case DATA_TYPE_NVLIST_ARRAY: {
3167168404Spjd		size_t nvsize = 0;
3168168404Spjd		int old_nvs_op = nvs->nvs_op;
3169168404Spjd		int err;
3170168404Spjd
3171168404Spjd		nvs->nvs_op = NVS_OP_GETSIZE;
3172168404Spjd		if (type == DATA_TYPE_NVLIST)
3173168404Spjd			err = nvs_operation(nvs, EMBEDDED_NVL(nvp), &nvsize);
3174168404Spjd		else
3175168404Spjd			err = nvs_embedded_nvl_array(nvs, nvp, &nvsize);
3176168404Spjd		nvs->nvs_op = old_nvs_op;
3177168404Spjd
3178168404Spjd		if (err != 0)
3179168404Spjd			return (EINVAL);
3180168404Spjd
3181168404Spjd		nvp_sz += nvsize;
3182168404Spjd		break;
3183168404Spjd	}
3184168404Spjd
3185168404Spjd	default:
3186168404Spjd		return (EINVAL);
3187168404Spjd	}
3188168404Spjd
3189168404Spjd	if (nvp_sz > INT32_MAX)
3190168404Spjd		return (EINVAL);
3191168404Spjd
3192168404Spjd	*size = nvp_sz;
3193168404Spjd
3194168404Spjd	return (0);
3195168404Spjd}
3196168404Spjd
3197168404Spjd
3198168404Spjd/*
3199168404Spjd * The NVS_XDR_MAX_LEN macro takes a packed xdr buffer of size x and estimates
3200168404Spjd * the largest nvpair that could be encoded in the buffer.
3201168404Spjd *
3202168404Spjd * See comments above nvpair_xdr_op() for the format of xdr encoding.
3203168404Spjd * The size of a xdr packed nvpair without any data is 5 words.
3204168404Spjd *
3205168404Spjd * Using the size of the data directly as an estimate would be ok
3206168404Spjd * in all cases except one.  If the data type is of DATA_TYPE_STRING_ARRAY
3207168404Spjd * then the actual nvpair has space for an array of pointers to index
3208168404Spjd * the strings.  These pointers are not encoded into the packed xdr buffer.
3209168404Spjd *
3210168404Spjd * If the data is of type DATA_TYPE_STRING_ARRAY and all the strings are
3211168404Spjd * of length 0, then each string is endcoded in xdr format as a single word.
3212168404Spjd * Therefore when expanded to an nvpair there will be 2.25 word used for
3213168404Spjd * each string.  (a int64_t allocated for pointer usage, and a single char
3214168404Spjd * for the null termination.)
3215168404Spjd *
3216168404Spjd * This is the calculation performed by the NVS_XDR_MAX_LEN macro.
3217168404Spjd */
3218168404Spjd#define	NVS_XDR_HDR_LEN		((size_t)(5 * 4))
3219168404Spjd#define	NVS_XDR_DATA_LEN(y)	(((size_t)(y) <= NVS_XDR_HDR_LEN) ? \
3220168404Spjd					0 : ((size_t)(y) - NVS_XDR_HDR_LEN))
3221168404Spjd#define	NVS_XDR_MAX_LEN(x)	(NVP_SIZE_CALC(1, 0) + \
3222168404Spjd					(NVS_XDR_DATA_LEN(x) * 2) + \
3223168404Spjd					NV_ALIGN4((NVS_XDR_DATA_LEN(x) / 4)))
3224168404Spjd
3225168404Spjdstatic int
3226168404Spjdnvs_xdr_nvpair(nvstream_t *nvs, nvpair_t *nvp, size_t *size)
3227168404Spjd{
3228168404Spjd	XDR 	*xdr = nvs->nvs_private;
3229168404Spjd	int32_t	encode_len, decode_len;
3230168404Spjd
3231168404Spjd	switch (nvs->nvs_op) {
3232168404Spjd	case NVS_OP_ENCODE: {
3233168404Spjd		size_t nvsize;
3234168404Spjd
3235168404Spjd		if (nvs_xdr_nvp_size(nvs, nvp, &nvsize) != 0)
3236168404Spjd			return (EFAULT);
3237168404Spjd
3238168404Spjd		decode_len = nvp->nvp_size;
3239168404Spjd		encode_len = nvsize;
3240168404Spjd		if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len))
3241168404Spjd			return (EFAULT);
3242168404Spjd
3243168404Spjd		return (nvs_xdr_nvp_op(nvs, nvp));
3244168404Spjd	}
3245168404Spjd	case NVS_OP_DECODE: {
3246168404Spjd		struct xdr_bytesrec bytesrec;
3247168404Spjd
3248168404Spjd		/* get the encode and decode size */
3249168404Spjd		if (!xdr_int(xdr, &encode_len) || !xdr_int(xdr, &decode_len))
3250168404Spjd			return (EFAULT);
3251168404Spjd		*size = decode_len;
3252168404Spjd
3253168404Spjd		/* are we at the end of the stream? */
3254168404Spjd		if (*size == 0)
3255168404Spjd			return (0);
3256168404Spjd
3257168404Spjd		/* sanity check the size parameter */
3258168404Spjd		if (!xdr_control(xdr, XDR_GET_BYTES_AVAIL, &bytesrec))
3259168404Spjd			return (EFAULT);
3260168404Spjd
3261168404Spjd		if (*size > NVS_XDR_MAX_LEN(bytesrec.xc_num_avail))
3262168404Spjd			return (EFAULT);
3263168404Spjd		break;
3264168404Spjd	}
3265168404Spjd
3266168404Spjd	default:
3267168404Spjd		return (EINVAL);
3268168404Spjd	}
3269168404Spjd	return (0);
3270168404Spjd}
3271168404Spjd
3272168404Spjdstatic const struct nvs_ops nvs_xdr_ops = {
3273168404Spjd	nvs_xdr_nvlist,
3274168404Spjd	nvs_xdr_nvpair,
3275168404Spjd	nvs_xdr_nvp_op,
3276168404Spjd	nvs_xdr_nvp_size,
3277168404Spjd	nvs_xdr_nvl_fini
3278168404Spjd};
3279168404Spjd
3280168404Spjdstatic int
3281168404Spjdnvs_xdr(nvstream_t *nvs, nvlist_t *nvl, char *buf, size_t *buflen)
3282168404Spjd{
3283168404Spjd	XDR xdr;
3284168404Spjd	int err;
3285168404Spjd
3286168404Spjd	nvs->nvs_ops = &nvs_xdr_ops;
3287168404Spjd
3288168404Spjd	if ((err = nvs_xdr_create(nvs, &xdr, buf + sizeof (nvs_header_t),
3289168404Spjd	    *buflen - sizeof (nvs_header_t))) != 0)
3290168404Spjd		return (err);
3291168404Spjd
3292168404Spjd	err = nvs_operation(nvs, nvl, buflen);
3293168404Spjd
3294168404Spjd	nvs_xdr_destroy(nvs);
3295168404Spjd
3296168404Spjd	return (err);
3297168404Spjd}
3298