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  */
26 int
lzbe_nvlist_get(const char * pool,const char * key,void ** ptr)27 lzbe_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 
73 int
lzbe_nvlist_set(const char * pool,const char * key,void * ptr)74 lzbe_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  */
130 void
lzbe_nvlist_free(void * ptr)131 lzbe_nvlist_free(void *ptr)
132 {
133 	nvlist_free(ptr);
134 }
135 
136 static 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 
166 static int
nvpair_type_from_name(const char * name)167 nvpair_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  */
181 int
lzbe_add_pair(void * ptr,const char * key,const char * type,void * value,size_t size)182 lzbe_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 
343 int
lzbe_remove_pair(void * ptr,const char * key)344 lzbe_remove_pair(void *ptr, const char *key)
345 {
346 
347 	return (nvlist_remove_all(ptr, key));
348 }
349