1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11/*
12 * Copyright 2020 Toomas Soome <tsoome@me.com>
13 */
14
15#include <sys/types.h>
16#include <string.h>
17#include <libzfs.h>
18#include <libzfsbootenv.h>
19#include <sys/zfs_bootenv.h>
20#include <sys/vdev_impl.h>
21
22/*
23 * Get or create nvlist. If key is not NULL, get nvlist from bootenv,
24 * otherwise return bootenv.
25 */
26int
27lzbe_nvlist_get(const char *pool, const char *key, void **ptr)
28{
29	libzfs_handle_t *hdl;
30	zpool_handle_t *zphdl;
31	nvlist_t *nv;
32	int rv = -1;
33
34	if (pool == NULL || *pool == '\0')
35		return (rv);
36
37	if ((hdl = libzfs_init()) == NULL) {
38		return (rv);
39	}
40
41	zphdl = zpool_open(hdl, pool);
42	if (zphdl == NULL) {
43		libzfs_fini(hdl);
44		return (rv);
45	}
46
47	rv = zpool_get_bootenv(zphdl, &nv);
48	if (rv == 0) {
49		nvlist_t *nvl, *dup;
50
51		if (key != NULL) {
52			rv = nvlist_lookup_nvlist(nv, key, &nvl);
53			if (rv == 0) {
54				rv = nvlist_dup(nvl, &dup, 0);
55				nvlist_free(nv);
56				if (rv == 0)
57					nv = dup;
58				else
59					nv = NULL;
60			} else {
61				nvlist_free(nv);
62				rv = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0);
63			}
64		}
65		*ptr = nv;
66	}
67
68	zpool_close(zphdl);
69	libzfs_fini(hdl);
70	return (rv);
71}
72
73int
74lzbe_nvlist_set(const char *pool, const char *key, void *ptr)
75{
76	libzfs_handle_t *hdl;
77	zpool_handle_t *zphdl;
78	nvlist_t *nv;
79	uint64_t version;
80	int rv = -1;
81
82	if (pool == NULL || *pool == '\0')
83		return (rv);
84
85	if ((hdl = libzfs_init()) == NULL) {
86		return (rv);
87	}
88
89	zphdl = zpool_open(hdl, pool);
90	if (zphdl == NULL) {
91		libzfs_fini(hdl);
92		return (rv);
93	}
94
95	if (key != NULL) {
96		rv = zpool_get_bootenv(zphdl, &nv);
97		if (rv == 0) {
98			/*
99			 * We got the nvlist, check for version.
100			 * if version is missing or is not VB_NVLIST,
101			 * create new list.
102			 */
103			rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION,
104			    &version);
105			if (rv != 0 || version != VB_NVLIST) {
106				/* Drop this nvlist */
107				fnvlist_free(nv);
108				/* Create and prepare new nvlist */
109				nv = fnvlist_alloc();
110				fnvlist_add_uint64(nv, BOOTENV_VERSION,
111				    VB_NVLIST);
112			}
113			rv = nvlist_add_nvlist(nv, key, ptr);
114			if (rv == 0)
115				rv = zpool_set_bootenv(zphdl, nv);
116			nvlist_free(nv);
117		}
118	} else {
119		rv = zpool_set_bootenv(zphdl, ptr);
120	}
121
122	zpool_close(zphdl);
123	libzfs_fini(hdl);
124	return (rv);
125}
126
127/*
128 * free nvlist we got via lzbe_nvlist_get()
129 */
130void
131lzbe_nvlist_free(void *ptr)
132{
133	nvlist_free(ptr);
134}
135
136static const char *typenames[] = {
137	"DATA_TYPE_UNKNOWN",
138	"DATA_TYPE_BOOLEAN",
139	"DATA_TYPE_BYTE",
140	"DATA_TYPE_INT16",
141	"DATA_TYPE_UINT16",
142	"DATA_TYPE_INT32",
143	"DATA_TYPE_UINT32",
144	"DATA_TYPE_INT64",
145	"DATA_TYPE_UINT64",
146	"DATA_TYPE_STRING",
147	"DATA_TYPE_BYTE_ARRAY",
148	"DATA_TYPE_INT16_ARRAY",
149	"DATA_TYPE_UINT16_ARRAY",
150	"DATA_TYPE_INT32_ARRAY",
151	"DATA_TYPE_UINT32_ARRAY",
152	"DATA_TYPE_INT64_ARRAY",
153	"DATA_TYPE_UINT64_ARRAY",
154	"DATA_TYPE_STRING_ARRAY",
155	"DATA_TYPE_HRTIME",
156	"DATA_TYPE_NVLIST",
157	"DATA_TYPE_NVLIST_ARRAY",
158	"DATA_TYPE_BOOLEAN_VALUE",
159	"DATA_TYPE_INT8",
160	"DATA_TYPE_UINT8",
161	"DATA_TYPE_BOOLEAN_ARRAY",
162	"DATA_TYPE_INT8_ARRAY",
163	"DATA_TYPE_UINT8_ARRAY"
164};
165
166static int
167nvpair_type_from_name(const char *name)
168{
169	unsigned i;
170
171	for (i = 0; i < ARRAY_SIZE(typenames); i++) {
172		if (strcmp(name, typenames[i]) == 0)
173			return (i);
174	}
175	return (0);
176}
177
178/*
179 * Add pair defined by key, type and value into nvlist.
180 */
181int
182lzbe_add_pair(void *ptr, const char *key, const char *type, void *value,
183    size_t size)
184{
185	nvlist_t *nv = ptr;
186	data_type_t dt;
187	int rv = 0;
188
189	if (ptr == NULL || key == NULL || value == NULL)
190		return (rv);
191
192	if (type == NULL)
193		type = "DATA_TYPE_STRING";
194	dt = nvpair_type_from_name(type);
195	if (dt == DATA_TYPE_UNKNOWN)
196		return (EINVAL);
197
198	switch (dt) {
199	case DATA_TYPE_BYTE:
200		if (size != sizeof (uint8_t)) {
201			rv = EINVAL;
202			break;
203		}
204		rv = nvlist_add_byte(nv, key, *(uint8_t *)value);
205		break;
206
207	case DATA_TYPE_INT16:
208		if (size != sizeof (int16_t)) {
209			rv = EINVAL;
210			break;
211		}
212		rv = nvlist_add_int16(nv, key, *(int16_t *)value);
213		break;
214
215	case DATA_TYPE_UINT16:
216		if (size != sizeof (uint16_t)) {
217			rv = EINVAL;
218			break;
219		}
220		rv = nvlist_add_uint16(nv, key, *(uint16_t *)value);
221		break;
222
223	case DATA_TYPE_INT32:
224		if (size != sizeof (int32_t)) {
225			rv = EINVAL;
226			break;
227		}
228		rv = nvlist_add_int32(nv, key, *(int32_t *)value);
229		break;
230
231	case DATA_TYPE_UINT32:
232		if (size != sizeof (uint32_t)) {
233			rv = EINVAL;
234			break;
235		}
236		rv = nvlist_add_uint32(nv, key, *(uint32_t *)value);
237		break;
238
239	case DATA_TYPE_INT64:
240		if (size != sizeof (int64_t)) {
241			rv = EINVAL;
242			break;
243		}
244		rv = nvlist_add_int64(nv, key, *(int64_t *)value);
245		break;
246
247	case DATA_TYPE_UINT64:
248		if (size != sizeof (uint64_t)) {
249			rv = EINVAL;
250			break;
251		}
252		rv = nvlist_add_uint64(nv, key, *(uint64_t *)value);
253		break;
254
255	case DATA_TYPE_STRING:
256		rv = nvlist_add_string(nv, key, value);
257		break;
258
259	case DATA_TYPE_BYTE_ARRAY:
260		rv = nvlist_add_byte_array(nv, key, value, size);
261		break;
262
263	case DATA_TYPE_INT16_ARRAY:
264		rv = nvlist_add_int16_array(nv, key, value, size);
265		break;
266
267	case DATA_TYPE_UINT16_ARRAY:
268		rv = nvlist_add_uint16_array(nv, key, value, size);
269		break;
270
271	case DATA_TYPE_INT32_ARRAY:
272		rv = nvlist_add_int32_array(nv, key, value, size);
273		break;
274
275	case DATA_TYPE_UINT32_ARRAY:
276		rv = nvlist_add_uint32_array(nv, key, value, size);
277		break;
278
279	case DATA_TYPE_INT64_ARRAY:
280		rv = nvlist_add_int64_array(nv, key, value, size);
281		break;
282
283	case DATA_TYPE_UINT64_ARRAY:
284		rv = nvlist_add_uint64_array(nv, key, value, size);
285		break;
286
287	case DATA_TYPE_STRING_ARRAY:
288		rv = nvlist_add_string_array(nv, key, value, size);
289		break;
290
291	case DATA_TYPE_NVLIST:
292		rv = nvlist_add_nvlist(nv, key, (nvlist_t *)value);
293		break;
294
295	case DATA_TYPE_NVLIST_ARRAY:
296		rv = nvlist_add_nvlist_array(nv, key, (const nvlist_t **)value,
297		    size);
298		break;
299
300	case DATA_TYPE_BOOLEAN_VALUE:
301		if (size != sizeof (boolean_t)) {
302			rv = EINVAL;
303			break;
304		}
305		rv = nvlist_add_boolean_value(nv, key, *(boolean_t *)value);
306		break;
307
308	case DATA_TYPE_INT8:
309		if (size != sizeof (int8_t)) {
310			rv = EINVAL;
311			break;
312		}
313		rv = nvlist_add_int8(nv, key, *(int8_t *)value);
314		break;
315
316	case DATA_TYPE_UINT8:
317		if (size != sizeof (uint8_t)) {
318			rv = EINVAL;
319			break;
320		}
321		rv = nvlist_add_uint8(nv, key, *(uint8_t *)value);
322		break;
323
324	case DATA_TYPE_BOOLEAN_ARRAY:
325		rv = nvlist_add_boolean_array(nv, key, value, size);
326		break;
327
328	case DATA_TYPE_INT8_ARRAY:
329		rv = nvlist_add_int8_array(nv, key, value, size);
330		break;
331
332	case DATA_TYPE_UINT8_ARRAY:
333		rv = nvlist_add_uint8_array(nv, key, value, size);
334		break;
335
336	default:
337		return (ENOTSUP);
338	}
339
340	return (rv);
341}
342
343int
344lzbe_remove_pair(void *ptr, const char *key)
345{
346
347	return (nvlist_remove_all(ptr, key));
348}
349