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