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