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 <sys/sysmacros.h>
17 #include <string.h>
18 #include <libzfs.h>
19 #include <libzfsbootenv.h>
20 #include <sys/zfs_bootenv.h>
21 #include <sys/vdev_impl.h>
22 
23 /*
24  * Get or create nvlist. If key is not NULL, get nvlist from bootenv,
25  * otherwise return bootenv.
26  */
27 int
28 lzbe_nvlist_get(const char *pool, const char *key, void **ptr)
29 {
30 	libzfs_handle_t *hdl;
31 	zpool_handle_t *zphdl;
32 	nvlist_t *nv;
33 	int rv = -1;
34 
35 	if (pool == NULL || *pool == '\0')
36 		return (rv);
37 
38 	if ((hdl = libzfs_init()) == NULL) {
39 		return (rv);
40 	}
41 
42 	zphdl = zpool_open(hdl, pool);
43 	if (zphdl == NULL) {
44 		libzfs_fini(hdl);
45 		return (rv);
46 	}
47 
48 	rv = zpool_get_bootenv(zphdl, &nv);
49 	if (rv == 0) {
50 		nvlist_t *nvl, *dup;
51 
52 		if (key != NULL) {
53 			rv = nvlist_lookup_nvlist(nv, key, &nvl);
54 			if (rv == 0) {
55 				rv = nvlist_dup(nvl, &dup, 0);
56 				nvlist_free(nv);
57 				if (rv == 0)
58 					nv = dup;
59 				else
60 					nv = NULL;
61 			} else {
62 				nvlist_free(nv);
63 				rv = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0);
64 			}
65 		}
66 		*ptr = nv;
67 	}
68 
69 	zpool_close(zphdl);
70 	libzfs_fini(hdl);
71 	return (rv);
72 }
73 
74 int
75 lzbe_nvlist_set(const char *pool, const char *key, void *ptr)
76 {
77 	libzfs_handle_t *hdl;
78 	zpool_handle_t *zphdl;
79 	nvlist_t *nv;
80 	uint64_t version;
81 	int rv = -1;
82 
83 	if (pool == NULL || *pool == '\0')
84 		return (rv);
85 
86 	if ((hdl = libzfs_init()) == NULL) {
87 		return (rv);
88 	}
89 
90 	zphdl = zpool_open(hdl, pool);
91 	if (zphdl == NULL) {
92 		libzfs_fini(hdl);
93 		return (rv);
94 	}
95 
96 	if (key != NULL) {
97 		rv = zpool_get_bootenv(zphdl, &nv);
98 		if (rv == 0) {
99 			/*
100 			 * We got the nvlist, check for version.
101 			 * if version is missing or is not VB_NVLIST,
102 			 * create new list.
103 			 */
104 			rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION,
105 			    &version);
106 			if (rv != 0 || version != VB_NVLIST) {
107 				/* Drop this nvlist */
108 				fnvlist_free(nv);
109 				/* Create and prepare new nvlist */
110 				nv = fnvlist_alloc();
111 				fnvlist_add_uint64(nv, BOOTENV_VERSION,
112 				    VB_NVLIST);
113 			}
114 			rv = nvlist_add_nvlist(nv, key, ptr);
115 			if (rv == 0)
116 				rv = zpool_set_bootenv(zphdl, nv);
117 			nvlist_free(nv);
118 		}
119 	} else {
120 		rv = zpool_set_bootenv(zphdl, ptr);
121 	}
122 
123 	zpool_close(zphdl);
124 	libzfs_fini(hdl);
125 	return (rv);
126 }
127 
128 /*
129  * free nvlist we got via lzbe_nvlist_get()
130  */
131 void
132 lzbe_nvlist_free(void *ptr)
133 {
134 	nvlist_free(ptr);
135 }
136 
137 static const char *typenames[] = {
138 	"DATA_TYPE_UNKNOWN",
139 	"DATA_TYPE_BOOLEAN",
140 	"DATA_TYPE_BYTE",
141 	"DATA_TYPE_INT16",
142 	"DATA_TYPE_UINT16",
143 	"DATA_TYPE_INT32",
144 	"DATA_TYPE_UINT32",
145 	"DATA_TYPE_INT64",
146 	"DATA_TYPE_UINT64",
147 	"DATA_TYPE_STRING",
148 	"DATA_TYPE_BYTE_ARRAY",
149 	"DATA_TYPE_INT16_ARRAY",
150 	"DATA_TYPE_UINT16_ARRAY",
151 	"DATA_TYPE_INT32_ARRAY",
152 	"DATA_TYPE_UINT32_ARRAY",
153 	"DATA_TYPE_INT64_ARRAY",
154 	"DATA_TYPE_UINT64_ARRAY",
155 	"DATA_TYPE_STRING_ARRAY",
156 	"DATA_TYPE_HRTIME",
157 	"DATA_TYPE_NVLIST",
158 	"DATA_TYPE_NVLIST_ARRAY",
159 	"DATA_TYPE_BOOLEAN_VALUE",
160 	"DATA_TYPE_INT8",
161 	"DATA_TYPE_UINT8",
162 	"DATA_TYPE_BOOLEAN_ARRAY",
163 	"DATA_TYPE_INT8_ARRAY",
164 	"DATA_TYPE_UINT8_ARRAY"
165 };
166 
167 static int
168 nvpair_type_from_name(const char *name)
169 {
170 	unsigned i;
171 
172 	for (i = 0; i < ARRAY_SIZE(typenames); i++) {
173 		if (strcmp(name, typenames[i]) == 0)
174 			return (i);
175 	}
176 	return (0);
177 }
178 
179 /*
180  * Add pair defined by key, type and value into nvlist.
181  */
182 int
183 lzbe_add_pair(void *ptr, const char *key, const char *type, void *value,
184     size_t size)
185 {
186 	nvlist_t *nv = ptr;
187 	data_type_t dt;
188 	int rv = 0;
189 
190 	if (ptr == NULL || key == NULL || value == NULL)
191 		return (rv);
192 
193 	if (type == NULL)
194 		type = "DATA_TYPE_STRING";
195 	dt = nvpair_type_from_name(type);
196 	if (dt == DATA_TYPE_UNKNOWN)
197 		return (EINVAL);
198 
199 	switch (dt) {
200 	case DATA_TYPE_BYTE:
201 		if (size != sizeof (uint8_t)) {
202 			rv = EINVAL;
203 			break;
204 		}
205 		rv = nvlist_add_byte(nv, key, *(uint8_t *)value);
206 		break;
207 
208 	case DATA_TYPE_INT16:
209 		if (size != sizeof (int16_t)) {
210 			rv = EINVAL;
211 			break;
212 		}
213 		rv = nvlist_add_int16(nv, key, *(int16_t *)value);
214 		break;
215 
216 	case DATA_TYPE_UINT16:
217 		if (size != sizeof (uint16_t)) {
218 			rv = EINVAL;
219 			break;
220 		}
221 		rv = nvlist_add_uint16(nv, key, *(uint16_t *)value);
222 		break;
223 
224 	case DATA_TYPE_INT32:
225 		if (size != sizeof (int32_t)) {
226 			rv = EINVAL;
227 			break;
228 		}
229 		rv = nvlist_add_int32(nv, key, *(int32_t *)value);
230 		break;
231 
232 	case DATA_TYPE_UINT32:
233 		if (size != sizeof (uint32_t)) {
234 			rv = EINVAL;
235 			break;
236 		}
237 		rv = nvlist_add_uint32(nv, key, *(uint32_t *)value);
238 		break;
239 
240 	case DATA_TYPE_INT64:
241 		if (size != sizeof (int64_t)) {
242 			rv = EINVAL;
243 			break;
244 		}
245 		rv = nvlist_add_int64(nv, key, *(int64_t *)value);
246 		break;
247 
248 	case DATA_TYPE_UINT64:
249 		if (size != sizeof (uint64_t)) {
250 			rv = EINVAL;
251 			break;
252 		}
253 		rv = nvlist_add_uint64(nv, key, *(uint64_t *)value);
254 		break;
255 
256 	case DATA_TYPE_STRING:
257 		rv = nvlist_add_string(nv, key, value);
258 		break;
259 
260 	case DATA_TYPE_BYTE_ARRAY:
261 		rv = nvlist_add_byte_array(nv, key, value, size);
262 		break;
263 
264 	case DATA_TYPE_INT16_ARRAY:
265 		rv = nvlist_add_int16_array(nv, key, value, size);
266 		break;
267 
268 	case DATA_TYPE_UINT16_ARRAY:
269 		rv = nvlist_add_uint16_array(nv, key, value, size);
270 		break;
271 
272 	case DATA_TYPE_INT32_ARRAY:
273 		rv = nvlist_add_int32_array(nv, key, value, size);
274 		break;
275 
276 	case DATA_TYPE_UINT32_ARRAY:
277 		rv = nvlist_add_uint32_array(nv, key, value, size);
278 		break;
279 
280 	case DATA_TYPE_INT64_ARRAY:
281 		rv = nvlist_add_int64_array(nv, key, value, size);
282 		break;
283 
284 	case DATA_TYPE_UINT64_ARRAY:
285 		rv = nvlist_add_uint64_array(nv, key, value, size);
286 		break;
287 
288 	case DATA_TYPE_STRING_ARRAY:
289 		rv = nvlist_add_string_array(nv, key, value, size);
290 		break;
291 
292 	case DATA_TYPE_NVLIST:
293 		rv = nvlist_add_nvlist(nv, key, (nvlist_t *)value);
294 		break;
295 
296 	case DATA_TYPE_NVLIST_ARRAY:
297 		rv = nvlist_add_nvlist_array(nv, key, value, 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 
343 int
344 lzbe_remove_pair(void *ptr, const char *key)
345 {
346 
347 	return (nvlist_remove_all(ptr, key));
348 }
349