1 /*-
2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 */
29
30 #include <sys/param.h>
31 #include <sys/hash.h>
32 #include <sys/limits.h>
33 #include <sys/queue.h>
34
35 #ifdef _KERNEL
36
37 #include <sys/ctype.h>
38 #include <sys/systm.h>
39
40 #include <machine/_inttypes.h>
41
42 #else /* !_KERNEL */
43
44 #include <ctype.h>
45 #include <errno.h>
46 #include <inttypes.h>
47 #include <stdbool.h>
48 #include <stdio.h>
49 #include <stdint.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #endif /* _KERNEL */
54
55 #include "bhnd_nvram_private.h"
56 #include "bhnd_nvram_datavar.h"
57
58 #include "bhnd_nvram_storevar.h"
59
60 /*
61 * BHND NVRAM Store
62 *
63 * Manages in-memory and persistent representations of NVRAM data.
64 */
65
66 static int bhnd_nvstore_parse_data(
67 struct bhnd_nvram_store *sc);
68
69 static int bhnd_nvstore_parse_path_entries(
70 struct bhnd_nvram_store *sc);
71
72 static int bhnd_nvram_store_export_child(
73 struct bhnd_nvram_store *sc,
74 bhnd_nvstore_path *top,
75 bhnd_nvstore_path *child,
76 bhnd_nvram_plist *plist,
77 uint32_t flags);
78
79 static int bhnd_nvstore_export_merge(
80 struct bhnd_nvram_store *sc,
81 bhnd_nvstore_path *path,
82 bhnd_nvram_plist *merged,
83 uint32_t flags);
84
85 static int bhnd_nvstore_export_devpath_alias(
86 struct bhnd_nvram_store *sc,
87 bhnd_nvstore_path *path,
88 const char *devpath,
89 bhnd_nvram_plist *plist,
90 u_long *alias_val);
91
92 /**
93 * Allocate and initialize a new NVRAM data store instance.
94 *
95 * The caller is responsible for deallocating the instance via
96 * bhnd_nvram_store_free().
97 *
98 * @param[out] store On success, a pointer to the newly allocated NVRAM data
99 * instance.
100 * @param data The NVRAM data to be managed by the returned NVRAM data store
101 * instance.
102 *
103 * @retval 0 success
104 * @retval non-zero if an error occurs during allocation or initialization, a
105 * regular unix error code will be returned.
106 */
107 int
bhnd_nvram_store_new(struct bhnd_nvram_store ** store,struct bhnd_nvram_data * data)108 bhnd_nvram_store_new(struct bhnd_nvram_store **store,
109 struct bhnd_nvram_data *data)
110 {
111 struct bhnd_nvram_store *sc;
112 int error;
113
114 /* Allocate new instance */
115 sc = bhnd_nv_calloc(1, sizeof(*sc));
116 if (sc == NULL)
117 return (ENOMEM);
118
119 BHND_NVSTORE_LOCK_INIT(sc);
120 BHND_NVSTORE_LOCK(sc);
121
122 /* Initialize path hash table */
123 sc->num_paths = 0;
124 for (size_t i = 0; i < nitems(sc->paths); i++)
125 LIST_INIT(&sc->paths[i]);
126
127 /* Initialize alias hash table */
128 sc->num_aliases = 0;
129 for (size_t i = 0; i < nitems(sc->aliases); i++)
130 LIST_INIT(&sc->aliases[i]);
131
132 /* Retain the NVRAM data */
133 sc->data = bhnd_nvram_data_retain(data);
134 sc->data_caps = bhnd_nvram_data_caps(data);
135 sc->data_opts = bhnd_nvram_data_options(data);
136 if (sc->data_opts != NULL) {
137 bhnd_nvram_plist_retain(sc->data_opts);
138 } else {
139 sc->data_opts = bhnd_nvram_plist_new();
140 if (sc->data_opts == NULL) {
141 error = ENOMEM;
142 goto cleanup;
143 }
144 }
145
146 /* Register required root path */
147 error = bhnd_nvstore_register_path(sc, BHND_NVSTORE_ROOT_PATH,
148 BHND_NVSTORE_ROOT_PATH_LEN);
149 if (error)
150 goto cleanup;
151
152 sc->root_path = bhnd_nvstore_get_path(sc, BHND_NVSTORE_ROOT_PATH,
153 BHND_NVSTORE_ROOT_PATH_LEN);
154 BHND_NV_ASSERT(sc->root_path, ("missing root path"));
155
156 /* Parse all variables vended by our backing NVRAM data instance,
157 * generating all path entries, alias entries, and variable indexes */
158 if ((error = bhnd_nvstore_parse_data(sc)))
159 goto cleanup;
160
161 *store = sc;
162
163 BHND_NVSTORE_UNLOCK(sc);
164 return (0);
165
166 cleanup:
167 BHND_NVSTORE_UNLOCK(sc);
168 bhnd_nvram_store_free(sc);
169 return (error);
170 }
171
172 /**
173 * Allocate and initialize a new NVRAM data store instance, parsing the
174 * NVRAM data from @p io.
175 *
176 * The caller is responsible for deallocating the instance via
177 * bhnd_nvram_store_free().
178 *
179 * The NVRAM data mapped by @p io will be copied, and @p io may be safely
180 * deallocated after bhnd_nvram_store_new() returns.
181 *
182 * @param[out] store On success, a pointer to the newly allocated NVRAM data
183 * instance.
184 * @param io An I/O context mapping the NVRAM data to be copied and parsed.
185 * @param cls The NVRAM data class to be used when parsing @p io, or NULL
186 * to perform runtime identification of the appropriate data class.
187 *
188 * @retval 0 success
189 * @retval non-zero if an error occurs during allocation or initialization, a
190 * regular unix error code will be returned.
191 */
192 int
bhnd_nvram_store_parse_new(struct bhnd_nvram_store ** store,struct bhnd_nvram_io * io,bhnd_nvram_data_class * cls)193 bhnd_nvram_store_parse_new(struct bhnd_nvram_store **store,
194 struct bhnd_nvram_io *io, bhnd_nvram_data_class *cls)
195 {
196 struct bhnd_nvram_data *data;
197 int error;
198
199 /* Try to parse the data */
200 if ((error = bhnd_nvram_data_new(cls, &data, io)))
201 return (error);
202
203 /* Try to create our new store instance */
204 error = bhnd_nvram_store_new(store, data);
205 bhnd_nvram_data_release(data);
206
207 return (error);
208 }
209
210 /**
211 * Free an NVRAM store instance, releasing all associated resources.
212 *
213 * @param sc A store instance previously allocated via
214 * bhnd_nvram_store_new().
215 */
216 void
bhnd_nvram_store_free(struct bhnd_nvram_store * sc)217 bhnd_nvram_store_free(struct bhnd_nvram_store *sc)
218 {
219
220 /* Clean up alias hash table */
221 for (size_t i = 0; i < nitems(sc->aliases); i++) {
222 bhnd_nvstore_alias *alias, *anext;
223 LIST_FOREACH_SAFE(alias, &sc->aliases[i], na_link, anext)
224 bhnd_nv_free(alias);
225 }
226
227 /* Clean up path hash table */
228 for (size_t i = 0; i < nitems(sc->paths); i++) {
229 bhnd_nvstore_path *path, *pnext;
230 LIST_FOREACH_SAFE(path, &sc->paths[i], np_link, pnext)
231 bhnd_nvstore_path_free(path);
232 }
233
234 if (sc->data != NULL)
235 bhnd_nvram_data_release(sc->data);
236
237 if (sc->data_opts != NULL)
238 bhnd_nvram_plist_release(sc->data_opts);
239
240 BHND_NVSTORE_LOCK_DESTROY(sc);
241 bhnd_nv_free(sc);
242 }
243
244 /**
245 * Parse all variables vended by our backing NVRAM data instance,
246 * generating all path entries, alias entries, and variable indexes.
247 *
248 * @param sc The NVRAM store instance to be initialized with
249 * paths, aliases, and data parsed from its backing
250 * data.
251 *
252 * @retval 0 success
253 * @retval non-zero if an error occurs during parsing, a regular unix error
254 * code will be returned.
255 */
256 static int
bhnd_nvstore_parse_data(struct bhnd_nvram_store * sc)257 bhnd_nvstore_parse_data(struct bhnd_nvram_store *sc)
258 {
259 const char *name;
260 void *cookiep;
261 int error;
262
263 /* Parse and register all device paths and path aliases. This enables
264 * resolution of _forward_ references to device paths aliases when
265 * scanning variable entries below */
266 if ((error = bhnd_nvstore_parse_path_entries(sc)))
267 return (error);
268
269 /* Calculate the per-path variable counts, and report dangling alias
270 * references as an error. */
271 cookiep = NULL;
272 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
273 bhnd_nvstore_path *path;
274 bhnd_nvstore_name_info info;
275
276 /* Parse the name info */
277 error = bhnd_nvstore_parse_name_info(name,
278 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
279 if (error)
280 return (error);
281
282 switch (info.type) {
283 case BHND_NVSTORE_VAR:
284 /* Fetch referenced path */
285 path = bhnd_nvstore_var_get_path(sc, &info);
286 if (path == NULL) {
287 BHND_NV_LOG("variable '%s' has dangling "
288 "path reference\n", name);
289 return (EFTYPE);
290 }
291
292 /* Increment path variable count */
293 if (path->num_vars == SIZE_MAX) {
294 BHND_NV_LOG("more than SIZE_MAX variables in "
295 "path %s\n", path->path_str);
296 return (EFTYPE);
297 }
298 path->num_vars++;
299 break;
300
301 case BHND_NVSTORE_ALIAS_DECL:
302 /* Skip -- path alias already parsed and recorded */
303 break;
304 }
305 }
306
307 /* If the backing NVRAM data instance vends only a single root ("/")
308 * path, we may be able to skip generating an index for the root
309 * path */
310 if (sc->num_paths == 1) {
311 bhnd_nvstore_path *path;
312
313 /* If the backing instance provides its own name-based lookup
314 * indexing, we can skip generating a duplicate here */
315 if (sc->data_caps & BHND_NVRAM_DATA_CAP_INDEXED)
316 return (0);
317
318 /* If the sole root path contains fewer variables than the
319 * minimum indexing threshhold, we do not need to generate an
320 * index */
321 path = bhnd_nvstore_get_root_path(sc);
322 if (path->num_vars < BHND_NV_IDX_VAR_THRESHOLD)
323 return (0);
324 }
325
326 /* Allocate per-path index instances */
327 for (size_t i = 0; i < nitems(sc->paths); i++) {
328 bhnd_nvstore_path *path;
329
330 LIST_FOREACH(path, &sc->paths[i], np_link) {
331 path->index = bhnd_nvstore_index_new(path->num_vars);
332 if (path->index == NULL)
333 return (ENOMEM);
334 }
335 }
336
337 /* Populate per-path indexes */
338 cookiep = NULL;
339 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
340 bhnd_nvstore_name_info info;
341 bhnd_nvstore_path *path;
342
343 /* Parse the name info */
344 error = bhnd_nvstore_parse_name_info(name,
345 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
346 if (error)
347 return (error);
348
349 switch (info.type) {
350 case BHND_NVSTORE_VAR:
351 /* Fetch referenced path */
352 path = bhnd_nvstore_var_get_path(sc, &info);
353 BHND_NV_ASSERT(path != NULL,
354 ("dangling path reference"));
355
356 /* Append to index */
357 error = bhnd_nvstore_index_append(sc, path->index,
358 cookiep);
359 if (error)
360 return (error);
361 break;
362
363 case BHND_NVSTORE_ALIAS_DECL:
364 /* Skip */
365 break;
366 }
367 }
368
369 /* Prepare indexes for querying */
370 for (size_t i = 0; i < nitems(sc->paths); i++) {
371 bhnd_nvstore_path *path;
372
373 LIST_FOREACH(path, &sc->paths[i], np_link) {
374 error = bhnd_nvstore_index_prepare(sc, path->index);
375 if (error)
376 return (error);
377 }
378 }
379
380 return (0);
381 }
382
383 /**
384 * Parse and register path and path alias entries for all declarations found in
385 * the NVRAM data backing @p nvram.
386 *
387 * @param sc The NVRAM store instance.
388 *
389 * @retval 0 success
390 * @retval non-zero If parsing fails, a regular unix error code will be
391 * returned.
392 */
393 static int
bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store * sc)394 bhnd_nvstore_parse_path_entries(struct bhnd_nvram_store *sc)
395 {
396 const char *name;
397 void *cookiep;
398 int error;
399
400 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
401
402 /* Skip path registration if the data source does not support device
403 * paths. */
404 if (!(sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)) {
405 BHND_NV_ASSERT(sc->root_path != NULL, ("missing root path"));
406 return (0);
407 }
408
409 /* Otherwise, parse and register all paths and path aliases */
410 cookiep = NULL;
411 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
412 bhnd_nvstore_name_info info;
413
414 /* Parse the name info */
415 error = bhnd_nvstore_parse_name_info(name,
416 BHND_NVSTORE_NAME_INTERNAL, sc->data_caps, &info);
417 if (error)
418 return (error);
419
420 /* Register the path */
421 error = bhnd_nvstore_var_register_path(sc, &info, cookiep);
422 if (error) {
423 BHND_NV_LOG("failed to register path for %s: %d\n",
424 name, error);
425 return (error);
426 }
427 }
428
429 return (0);
430 }
431
432 /**
433 * Merge exported per-path variables (uncommitted, committed, or both) into
434 * the empty @p merged property list.
435 *
436 * @param sc The NVRAM store instance.
437 * @param path The NVRAM path to be exported.
438 * @param merged The property list to populate with the merged results.
439 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
440 *
441 * @retval 0 success
442 * @retval ENOMEM If allocation fails.
443 * @retval non-zero If merging the variables defined in @p path otherwise
444 * fails, a regular unix error code will be returned.
445 */
446 static int
bhnd_nvstore_export_merge(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path,bhnd_nvram_plist * merged,uint32_t flags)447 bhnd_nvstore_export_merge(struct bhnd_nvram_store *sc,
448 bhnd_nvstore_path *path, bhnd_nvram_plist *merged, uint32_t flags)
449 {
450 void *cookiep, *idxp;
451 int error;
452
453 /* Populate merged list with all pending variables */
454 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
455 bhnd_nvram_prop *prop;
456
457 prop = NULL;
458 while ((prop = bhnd_nvram_plist_next(path->pending, prop))) {
459 /* Skip variables marked for deletion */
460 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED)) {
461 if (bhnd_nvram_prop_is_null(prop))
462 continue;
463 }
464
465 /* Append to merged list */
466 error = bhnd_nvram_plist_append(merged, prop);
467 if (error)
468 return (error);
469 }
470 }
471
472 /* Skip merging committed variables? */
473 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED))
474 return (0);
475
476 /* Merge in the committed NVRAM variables */
477 idxp = NULL;
478 while ((cookiep = bhnd_nvstore_path_data_next(sc, path, &idxp))) {
479 const char *name;
480 bhnd_nvram_val *val;
481
482 /* Fetch the variable name */
483 name = bhnd_nvram_data_getvar_name(sc->data, cookiep);
484
485 /* Trim device path prefix */
486 if (sc->data_caps & BHND_NVRAM_DATA_CAP_DEVPATHS)
487 name = bhnd_nvram_trim_path_name(name);
488
489 /* Skip if already defined in pending updates */
490 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED)) {
491 if (bhnd_nvram_plist_contains(path->pending, name))
492 continue;
493 }
494
495 /* Skip if higher precedence value was already defined. This
496 * may occur if the underlying data store contains duplicate
497 * keys; iteration will always return the definition with
498 * the highest precedence first */
499 if (bhnd_nvram_plist_contains(merged, name))
500 continue;
501
502 /* Fetch the variable's value representation */
503 if ((error = bhnd_nvram_data_copy_val(sc->data, cookiep, &val)))
504 return (error);
505
506 /* Add to path variable list */
507 error = bhnd_nvram_plist_append_val(merged, name, val);
508 bhnd_nvram_val_release(val);
509 if (error)
510 return (error);
511 }
512
513 return (0);
514 }
515
516 /**
517 * Find a free alias value for @p path, and append the devpathXX alias
518 * declaration to @p plist.
519 *
520 * @param sc The NVRAM store instance.
521 * @param path The NVRAM path for which a devpath alias
522 * variable should be produced.
523 * @param devpath The devpathXX path value for @p path.
524 * @param plist The property list to which @p path's devpath
525 * variable will be appended.
526 * @param[out] alias_val On success, will be set to the alias value
527 * allocated for @p path.
528 *
529 * @retval 0 success
530 * @retval ENOMEM If allocation fails.
531 * @retval non-zero If merging the variables defined in @p path otherwise
532 * fails, a regular unix error code will be returned.
533 */
534 static int
bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store * sc,bhnd_nvstore_path * path,const char * devpath,bhnd_nvram_plist * plist,u_long * alias_val)535 bhnd_nvstore_export_devpath_alias(struct bhnd_nvram_store *sc,
536 bhnd_nvstore_path *path, const char *devpath, bhnd_nvram_plist *plist,
537 u_long *alias_val)
538 {
539 bhnd_nvstore_alias *alias;
540 char *pathvar;
541 int error;
542
543 *alias_val = 0;
544
545 /* Prefer alias value already reserved for this path. */
546 alias = bhnd_nvstore_find_alias(sc, path->path_str);
547 if (alias != NULL) {
548 *alias_val = alias->alias;
549
550 /* Allocate devpathXX variable name */
551 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
552 if (pathvar == NULL)
553 return (ENOMEM);
554
555 /* Append alias variable to property list */
556 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
557
558 BHND_NV_ASSERT(error != EEXIST, ("reserved alias %lu:%s in use",
559 * alias_val, path->path_str));
560
561 bhnd_nv_free(pathvar);
562 return (error);
563 }
564
565 /* Find the next free devpathXX alias entry */
566 while (1) {
567 /* Skip existing reserved alias values */
568 while (bhnd_nvstore_get_alias(sc, *alias_val) != NULL) {
569 if (*alias_val == ULONG_MAX)
570 return (ENOMEM);
571
572 (*alias_val)++;
573 }
574
575 /* Allocate devpathXX variable name */
576 bhnd_nv_asprintf(&pathvar, "devpath%lu", *alias_val);
577 if (pathvar == NULL)
578 return (ENOMEM);
579
580 /* If not in-use, we can terminate the search */
581 if (!bhnd_nvram_plist_contains(plist, pathvar))
582 break;
583
584 /* Keep searching */
585 bhnd_nv_free(pathvar);
586
587 if (*alias_val == ULONG_MAX)
588 return (ENOMEM);
589
590 (*alias_val)++;
591 }
592
593 /* Append alias variable to property list */
594 error = bhnd_nvram_plist_append_string(plist, pathvar, devpath);
595
596 bhnd_nv_free(pathvar);
597 return (error);
598 }
599
600 /**
601 * Export a single @p child path's properties, appending the result to @p plist.
602 *
603 * @param sc The NVRAM store instance.
604 * @param top The root NVRAM path being exported.
605 * @param child The NVRAM path to be exported.
606 * @param plist The property list to which @p child's exported
607 * properties should be appended.
608 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
609 *
610 * @retval 0 success
611 * @retval ENOMEM If allocation fails.
612 * @retval non-zero If merging the variables defined in @p path otherwise
613 * fails, a regular unix error code will be returned.
614 */
615 static int
bhnd_nvram_store_export_child(struct bhnd_nvram_store * sc,bhnd_nvstore_path * top,bhnd_nvstore_path * child,bhnd_nvram_plist * plist,uint32_t flags)616 bhnd_nvram_store_export_child(struct bhnd_nvram_store *sc,
617 bhnd_nvstore_path *top, bhnd_nvstore_path *child, bhnd_nvram_plist *plist,
618 uint32_t flags)
619 {
620 bhnd_nvram_plist *path_vars;
621 bhnd_nvram_prop *prop;
622 const char *relpath;
623 char *prefix, *namebuf;
624 size_t prefix_len, relpath_len;
625 size_t namebuf_size;
626 bool emit_compact_devpath;
627 int error;
628
629 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
630
631 prefix = NULL;
632 path_vars = NULL;
633 namebuf = NULL;
634
635 /* Determine the path relative to the top-level path */
636 relpath = bhnd_nvstore_parse_relpath(top->path_str, child->path_str);
637 if (relpath == NULL) {
638 /* Skip -- not a child of the root path */
639 return (0);
640 }
641 relpath_len = strlen(relpath);
642
643 /* Skip sub-path if export of children was not requested, */
644 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_CHILDREN) && relpath_len > 0)
645 return (0);
646
647 /* Collect all variables to be included in the export */
648 if ((path_vars = bhnd_nvram_plist_new()) == NULL)
649 return (ENOMEM);
650
651 if ((error = bhnd_nvstore_export_merge(sc, child, path_vars, flags))) {
652 bhnd_nvram_plist_release(path_vars);
653 return (error);
654 }
655
656 /* Skip if no children are to be exported */
657 if (bhnd_nvram_plist_count(path_vars) == 0) {
658 bhnd_nvram_plist_release(path_vars);
659 return (0);
660 }
661
662 /* Determine appropriate device path encoding */
663 emit_compact_devpath = false;
664 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS)) {
665 /* Re-encode as compact (if non-empty path) */
666 if (relpath_len > 0)
667 emit_compact_devpath = true;
668 } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS)) {
669 /* Re-encode with fully expanded device path */
670 emit_compact_devpath = false;
671 } else if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
672 /* Preserve existing encoding of this path */
673 if (bhnd_nvstore_find_alias(sc, child->path_str) != NULL)
674 emit_compact_devpath = true;
675 } else {
676 BHND_NV_LOG("invalid device path flag: %#" PRIx32, flags);
677 error = EINVAL;
678 goto finished;
679 }
680
681 /* Allocate variable device path prefix to use for all property names,
682 * and if using compact encoding, emit the devpathXX= variable */
683 prefix = NULL;
684 prefix_len = 0;
685 if (emit_compact_devpath) {
686 u_long alias_val;
687 int len;
688
689 /* Reserve an alias value and append the devpathXX= variable to
690 * the property list */
691 error = bhnd_nvstore_export_devpath_alias(sc, child, relpath,
692 plist, &alias_val);
693 if (error)
694 goto finished;
695
696 /* Allocate variable name prefix */
697 len = bhnd_nv_asprintf(&prefix, "%lu:", alias_val);
698 if (prefix == NULL) {
699 error = ENOMEM;
700 goto finished;
701 }
702
703 prefix_len = len;
704 } else if (relpath_len > 0) {
705 int len;
706
707 /* Allocate the variable name prefix, appending '/' to the
708 * relative path */
709 len = bhnd_nv_asprintf(&prefix, "%s/", relpath);
710 if (prefix == NULL) {
711 error = ENOMEM;
712 goto finished;
713 }
714
715 prefix_len = len;
716 }
717
718 /* If prefixing of variable names is required, allocate a name
719 * formatting buffer */
720 namebuf_size = 0;
721 if (prefix != NULL) {
722 size_t maxlen;
723
724 /* Find the maximum name length */
725 maxlen = 0;
726 prop = NULL;
727 while ((prop = bhnd_nvram_plist_next(path_vars, prop))) {
728 const char *name;
729
730 name = bhnd_nvram_prop_name(prop);
731 maxlen = bhnd_nv_ummax(strlen(name), maxlen);
732 }
733
734 /* Allocate name buffer (path-prefix + name + '\0') */
735 namebuf_size = prefix_len + maxlen + 1;
736 namebuf = bhnd_nv_malloc(namebuf_size);
737 if (namebuf == NULL) {
738 error = ENOMEM;
739 goto finished;
740 }
741 }
742
743 /* Append all path variables to the export plist, prepending the
744 * device-path prefix to the variable names, if required */
745 prop = NULL;
746 while ((prop = bhnd_nvram_plist_next(path_vars, prop)) != NULL) {
747 const char *name;
748
749 /* Prepend device prefix to the variable name */
750 name = bhnd_nvram_prop_name(prop);
751 if (prefix != NULL) {
752 int len;
753
754 /*
755 * Write prefixed variable name to our name buffer.
756 *
757 * We precalcuate the size when scanning all names
758 * above, so this should always succeed.
759 */
760 len = snprintf(namebuf, namebuf_size, "%s%s", prefix,
761 name);
762 if (len < 0 || (size_t)len >= namebuf_size)
763 BHND_NV_PANIC("invalid max_name_len");
764
765 name = namebuf;
766 }
767
768 /* Add property to export plist */
769 error = bhnd_nvram_plist_append_val(plist, name,
770 bhnd_nvram_prop_val(prop));
771 if (error)
772 goto finished;
773 }
774
775 /* Success */
776 error = 0;
777
778 finished:
779 if (prefix != NULL)
780 bhnd_nv_free(prefix);
781
782 if (namebuf != NULL)
783 bhnd_nv_free(namebuf);
784
785 if (path_vars != NULL)
786 bhnd_nvram_plist_release(path_vars);
787
788 return (error);
789 }
790
791 /**
792 * Export a flat, ordered NVRAM property list representation of all NVRAM
793 * properties at @p path.
794 *
795 * @param sc The NVRAM store instance.
796 * @param path The NVRAM path to export, or NULL to select the root
797 * path.
798 * @param[out] cls On success, will be set to the backing data class
799 * of @p sc. If the data class is are not desired,
800 * a NULL pointer may be provided.
801 * @param[out] props On success, will be set to a caller-owned property
802 * list containing the exported properties. The caller is
803 * responsible for releasing this value via
804 * bhnd_nvram_plist_release().
805 * @param[out] options On success, will be set to a caller-owned property
806 * list containing the current NVRAM serialization options
807 * for @p sc. The caller is responsible for releasing this
808 * value via bhnd_nvram_plist_release().
809 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
810 *
811 * @retval 0 success
812 * @retval EINVAL If @p flags is invalid.
813 * @retval ENOENT The requested path was not found.
814 * @retval ENOMEM If allocation fails.
815 * @retval non-zero If export of @p path otherwise fails, a regular unix
816 * error code will be returned.
817 */
818 int
bhnd_nvram_store_export(struct bhnd_nvram_store * sc,const char * path,bhnd_nvram_data_class ** cls,bhnd_nvram_plist ** props,bhnd_nvram_plist ** options,uint32_t flags)819 bhnd_nvram_store_export(struct bhnd_nvram_store *sc, const char *path,
820 bhnd_nvram_data_class **cls, bhnd_nvram_plist **props,
821 bhnd_nvram_plist **options, uint32_t flags)
822 {
823 bhnd_nvram_plist *unordered;
824 bhnd_nvstore_path *top;
825 bhnd_nvram_prop *prop;
826 const char *name;
827 void *cookiep;
828 size_t num_dpath_flags;
829 int error;
830
831 *props = NULL;
832 unordered = NULL;
833 num_dpath_flags = 0;
834 if (options != NULL)
835 *options = NULL;
836
837 /* Default to exporting root path */
838 if (path == NULL)
839 path = BHND_NVSTORE_ROOT_PATH;
840
841 /* Default to exporting all properties */
842 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMMITTED) &&
843 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_UNCOMMITTED))
844 {
845 flags |= BHND_NVSTORE_EXPORT_ALL_VARS;
846 }
847
848 /* Default to preserving the current device path encoding */
849 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS) &&
850 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
851 {
852 flags |= BHND_NVSTORE_EXPORT_PRESERVE_DEVPATHS;
853 }
854
855 /* Exactly one device path encoding flag must be set */
856 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_COMPACT_DEVPATHS))
857 num_dpath_flags++;
858
859 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_EXPAND_DEVPATHS))
860 num_dpath_flags++;
861
862 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS))
863 num_dpath_flags++;
864
865 if (num_dpath_flags != 1)
866 return (EINVAL);
867
868 /* If EXPORT_DELETED is set, EXPORT_UNCOMMITTED must be set too */
869 if (BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED) &&
870 !BHND_NVSTORE_GET_FLAG(flags, EXPORT_DELETED))
871 {
872 return (EINVAL);
873 }
874
875 /* Lock internal state before querying paths/properties */
876 BHND_NVSTORE_LOCK(sc);
877
878 /* Fetch referenced path */
879 top = bhnd_nvstore_get_path(sc, path, strlen(path));
880 if (top == NULL) {
881 error = ENOENT;
882 goto failed;
883 }
884
885 /* Allocate new, empty property list */
886 if ((unordered = bhnd_nvram_plist_new()) == NULL) {
887 error = ENOMEM;
888 goto failed;
889 }
890
891 /* Export the top-level path first */
892 error = bhnd_nvram_store_export_child(sc, top, top, unordered, flags);
893 if (error)
894 goto failed;
895
896 /* Attempt to export any children of the root path */
897 for (size_t i = 0; i < nitems(sc->paths); i++) {
898 bhnd_nvstore_path *child;
899
900 LIST_FOREACH(child, &sc->paths[i], np_link) {
901 /* Top-level path was already exported */
902 if (child == top)
903 continue;
904
905 error = bhnd_nvram_store_export_child(sc, top,
906 child, unordered, flags);
907 if (error)
908 goto failed;
909 }
910 }
911
912 /* If requested, provide the current class and serialization options */
913 if (cls != NULL)
914 *cls = bhnd_nvram_data_get_class(sc->data);
915
916 if (options != NULL)
917 *options = bhnd_nvram_plist_retain(sc->data_opts);
918
919 /*
920 * If we're re-encoding device paths, don't bother preserving the
921 * existing NVRAM variable order; our variable names will not match
922 * the existing backing NVRAM data.
923 */
924 if (!BHND_NVSTORE_GET_FLAG(flags, EXPORT_PRESERVE_DEVPATHS)) {
925 *props = unordered;
926 unordered = NULL;
927
928 goto finished;
929 }
930
931 /*
932 * Re-order the flattened output to match the existing NVRAM variable
933 * ordering.
934 *
935 * We append all new variables at the end of the input; this should
936 * reduce the delta that needs to be written (e.g. to flash) when
937 * committing NVRAM updates, and should result in a serialization
938 * identical to the input serialization if uncommitted updates are
939 * excluded from the export.
940 */
941 if ((*props = bhnd_nvram_plist_new()) == NULL) {
942 error = ENOMEM;
943 goto failed;
944 }
945
946 /* Using the backing NVRAM data ordering to order all variables
947 * currently defined in the backing store */
948 cookiep = NULL;
949 while ((name = bhnd_nvram_data_next(sc->data, &cookiep))) {
950 prop = bhnd_nvram_plist_get_prop(unordered, name);
951 if (prop == NULL)
952 continue;
953
954 /* Append to ordered result */
955 if ((error = bhnd_nvram_plist_append(*props, prop)))
956 goto failed;
957
958 /* Remove from unordered list */
959 bhnd_nvram_plist_remove(unordered, name);
960 }
961
962 /* Any remaining variables are new, and should be appended to the
963 * end of the export list */
964 prop = NULL;
965 while ((prop = bhnd_nvram_plist_next(unordered, prop)) != NULL) {
966 if ((error = bhnd_nvram_plist_append(*props, prop)))
967 goto failed;
968 }
969
970 /* Export complete */
971 finished:
972 BHND_NVSTORE_UNLOCK(sc);
973
974 if (unordered != NULL)
975 bhnd_nvram_plist_release(unordered);
976
977 return (0);
978
979 failed:
980 BHND_NVSTORE_UNLOCK(sc);
981
982 if (unordered != NULL)
983 bhnd_nvram_plist_release(unordered);
984
985 if (options != NULL && *options != NULL)
986 bhnd_nvram_plist_release(*options);
987
988 if (*props != NULL)
989 bhnd_nvram_plist_release(*props);
990
991 return (error);
992 }
993
994 /**
995 * Encode all NVRAM properties at @p path, using the @p store's current NVRAM
996 * data format.
997 *
998 * @param sc The NVRAM store instance.
999 * @param path The NVRAM path to export, or NULL to select the root
1000 * path.
1001 * @param[out] data On success, will be set to the newly serialized value.
1002 * The caller is responsible for freeing this value
1003 * via bhnd_nvram_io_free().
1004 * @param flags Export flags. See BHND_NVSTORE_EXPORT_*.
1005 *
1006 * @retval 0 success
1007 * @retval EINVAL If @p flags is invalid.
1008 * @retval ENOENT The requested path was not found.
1009 * @retval ENOMEM If allocation fails.
1010 * @retval non-zero If serialization of @p path otherwise fails, a regular
1011 * unix error code will be returned.
1012 */
1013 int
bhnd_nvram_store_serialize(struct bhnd_nvram_store * sc,const char * path,struct bhnd_nvram_io ** data,uint32_t flags)1014 bhnd_nvram_store_serialize(struct bhnd_nvram_store *sc, const char *path,
1015 struct bhnd_nvram_io **data, uint32_t flags)
1016 {
1017 bhnd_nvram_plist *props;
1018 bhnd_nvram_plist *options;
1019 bhnd_nvram_data_class *cls;
1020 struct bhnd_nvram_io *io;
1021 void *outp;
1022 size_t olen;
1023 int error;
1024
1025 props = NULL;
1026 options = NULL;
1027 io = NULL;
1028
1029 /* Perform requested export */
1030 error = bhnd_nvram_store_export(sc, path, &cls, &props, &options,
1031 flags);
1032 if (error)
1033 return (error);
1034
1035 /* Determine serialized size */
1036 error = bhnd_nvram_data_serialize(cls, props, options, NULL, &olen);
1037 if (error)
1038 goto failed;
1039
1040 /* Allocate output buffer */
1041 if ((io = bhnd_nvram_iobuf_empty(olen, olen)) == NULL) {
1042 error = ENOMEM;
1043 goto failed;
1044 }
1045
1046 /* Fetch write pointer */
1047 if ((error = bhnd_nvram_io_write_ptr(io, 0, &outp, olen, NULL)))
1048 goto failed;
1049
1050 /* Perform serialization */
1051 error = bhnd_nvram_data_serialize(cls, props, options, outp, &olen);
1052 if (error)
1053 goto failed;
1054
1055 if ((error = bhnd_nvram_io_setsize(io, olen)))
1056 goto failed;
1057
1058 /* Success */
1059 bhnd_nvram_plist_release(props);
1060 bhnd_nvram_plist_release(options);
1061
1062 *data = io;
1063 return (0);
1064
1065 failed:
1066 if (props != NULL)
1067 bhnd_nvram_plist_release(props);
1068
1069 if (options != NULL)
1070 bhnd_nvram_plist_release(options);
1071
1072 if (io != NULL)
1073 bhnd_nvram_io_free(io);
1074
1075 return (error);
1076 }
1077
1078 /**
1079 * Read an NVRAM variable.
1080 *
1081 * @param sc The NVRAM parser state.
1082 * @param name The NVRAM variable name.
1083 * @param[out] outp On success, the requested value will be written
1084 * to this buffer. This argment may be NULL if
1085 * the value is not desired.
1086 * @param[in,out] olen The capacity of @p outp. On success, will be set
1087 * to the actual size of the requested value.
1088 * @param otype The requested data type to be written to
1089 * @p outp.
1090 *
1091 * @retval 0 success
1092 * @retval ENOENT The requested variable was not found.
1093 * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too
1094 * small to hold the requested value.
1095 * @retval non-zero If reading @p name otherwise fails, a regular unix
1096 * error code will be returned.
1097 */
1098 int
bhnd_nvram_store_getvar(struct bhnd_nvram_store * sc,const char * name,void * outp,size_t * olen,bhnd_nvram_type otype)1099 bhnd_nvram_store_getvar(struct bhnd_nvram_store *sc, const char *name,
1100 void *outp, size_t *olen, bhnd_nvram_type otype)
1101 {
1102 bhnd_nvstore_name_info info;
1103 bhnd_nvstore_path *path;
1104 bhnd_nvram_prop *prop;
1105 void *cookiep;
1106 int error;
1107
1108 BHND_NVSTORE_LOCK(sc);
1109
1110 /* Parse the variable name */
1111 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1112 sc->data_caps, &info);
1113 if (error)
1114 goto finished;
1115
1116 /* Fetch the variable's enclosing path entry */
1117 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL) {
1118 error = ENOENT;
1119 goto finished;
1120 }
1121
1122 /* Search uncommitted updates first */
1123 prop = bhnd_nvstore_path_get_update(sc, path, info.name);
1124 if (prop != NULL) {
1125 if (bhnd_nvram_prop_is_null(prop)) {
1126 /* NULL denotes a pending deletion */
1127 error = ENOENT;
1128 } else {
1129 error = bhnd_nvram_prop_encode(prop, outp, olen, otype);
1130 }
1131 goto finished;
1132 }
1133
1134 /* Search the backing NVRAM data */
1135 cookiep = bhnd_nvstore_path_data_lookup(sc, path, info.name);
1136 if (cookiep != NULL) {
1137 /* Found in backing store */
1138 error = bhnd_nvram_data_getvar(sc->data, cookiep, outp, olen,
1139 otype);
1140 goto finished;
1141 }
1142
1143 /* Not found */
1144 error = ENOENT;
1145
1146 finished:
1147 BHND_NVSTORE_UNLOCK(sc);
1148 return (error);
1149 }
1150
1151 /**
1152 * Common bhnd_nvram_store_set*() and bhnd_nvram_store_unsetvar()
1153 * implementation.
1154 *
1155 * If @p value is NULL, the variable will be marked for deletion.
1156 */
1157 static int
bhnd_nvram_store_setval_common(struct bhnd_nvram_store * sc,const char * name,bhnd_nvram_val * value)1158 bhnd_nvram_store_setval_common(struct bhnd_nvram_store *sc, const char *name,
1159 bhnd_nvram_val *value)
1160 {
1161 bhnd_nvstore_path *path;
1162 bhnd_nvstore_name_info info;
1163 int error;
1164
1165 BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
1166
1167 /* Parse the variable name */
1168 error = bhnd_nvstore_parse_name_info(name, BHND_NVSTORE_NAME_EXTERNAL,
1169 sc->data_caps, &info);
1170 if (error)
1171 return (error);
1172
1173 /* Fetch the variable's enclosing path entry */
1174 if ((path = bhnd_nvstore_var_get_path(sc, &info)) == NULL)
1175 return (error);
1176
1177 /* Register the update entry */
1178 return (bhnd_nvstore_path_register_update(sc, path, info.name, value));
1179 }
1180
1181 /**
1182 * Set an NVRAM variable.
1183 *
1184 * @param sc The NVRAM parser state.
1185 * @param name The NVRAM variable name.
1186 * @param value The new value.
1187 *
1188 * @retval 0 success
1189 * @retval ENOENT The requested variable @p name was not found.
1190 * @retval EINVAL If @p value is invalid.
1191 */
1192 int
bhnd_nvram_store_setval(struct bhnd_nvram_store * sc,const char * name,bhnd_nvram_val * value)1193 bhnd_nvram_store_setval(struct bhnd_nvram_store *sc, const char *name,
1194 bhnd_nvram_val *value)
1195 {
1196 int error;
1197
1198 BHND_NVSTORE_LOCK(sc);
1199 error = bhnd_nvram_store_setval_common(sc, name, value);
1200 BHND_NVSTORE_UNLOCK(sc);
1201
1202 return (error);
1203 }
1204
1205 /**
1206 * Set an NVRAM variable.
1207 *
1208 * @param sc The NVRAM parser state.
1209 * @param name The NVRAM variable name.
1210 * @param[out] inp The new value.
1211 * @param[in,out] ilen The size of @p inp.
1212 * @param itype The data type of @p inp.
1213 *
1214 * @retval 0 success
1215 * @retval ENOENT The requested variable @p name was not found.
1216 * @retval EINVAL If the new value is invalid.
1217 * @retval EINVAL If @p name is read-only.
1218 */
1219 int
bhnd_nvram_store_setvar(struct bhnd_nvram_store * sc,const char * name,const void * inp,size_t ilen,bhnd_nvram_type itype)1220 bhnd_nvram_store_setvar(struct bhnd_nvram_store *sc, const char *name,
1221 const void *inp, size_t ilen, bhnd_nvram_type itype)
1222 {
1223 bhnd_nvram_val val;
1224 int error;
1225
1226 error = bhnd_nvram_val_init(&val, NULL, inp, ilen, itype,
1227 BHND_NVRAM_VAL_FIXED|BHND_NVRAM_VAL_BORROW_DATA);
1228 if (error) {
1229 BHND_NV_LOG("error initializing value: %d\n", error);
1230 return (EINVAL);
1231 }
1232
1233 BHND_NVSTORE_LOCK(sc);
1234 error = bhnd_nvram_store_setval_common(sc, name, &val);
1235 BHND_NVSTORE_UNLOCK(sc);
1236
1237 bhnd_nvram_val_release(&val);
1238
1239 return (error);
1240 }
1241
1242 /**
1243 * Unset an NVRAM variable.
1244 *
1245 * @param sc The NVRAM parser state.
1246 * @param name The NVRAM variable name.
1247 *
1248 * @retval 0 success
1249 * @retval ENOENT The requested variable @p name was not found.
1250 * @retval EINVAL If @p name is read-only.
1251 */
1252 int
bhnd_nvram_store_unsetvar(struct bhnd_nvram_store * sc,const char * name)1253 bhnd_nvram_store_unsetvar(struct bhnd_nvram_store *sc, const char *name)
1254 {
1255 int error;
1256
1257 BHND_NVSTORE_LOCK(sc);
1258 error = bhnd_nvram_store_setval_common(sc, name, BHND_NVRAM_VAL_NULL);
1259 BHND_NVSTORE_UNLOCK(sc);
1260
1261 return (error);
1262 }
1263