LCOV - code coverage report
Current view: top level - libsystemd/sd-hwdb - hwdb-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 309 360 85.8 %
Date: 2019-08-22 15:41:25 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <ctype.h>
       4             : #include <stdio.h>
       5             : #include <sys/stat.h>
       6             : 
       7             : #include "alloc-util.h"
       8             : #include "conf-files.h"
       9             : #include "fd-util.h"
      10             : #include "fileio.h"
      11             : #include "fs-util.h"
      12             : #include "hwdb-internal.h"
      13             : #include "hwdb-util.h"
      14             : #include "label.h"
      15             : #include "mkdir.h"
      16             : #include "path-util.h"
      17             : #include "sort-util.h"
      18             : #include "strbuf.h"
      19             : #include "string-util.h"
      20             : #include "strv.h"
      21             : #include "tmpfile-util.h"
      22             : 
      23             : static const char *default_hwdb_bin_dir = "/etc/udev";
      24             : static const char * const conf_file_dirs[] = {
      25             :         "/etc/udev/hwdb.d",
      26             :         UDEVLIBEXECDIR "/hwdb.d",
      27             :         NULL
      28             : };
      29             : 
      30             : /*
      31             :  * Generic udev properties, key-value database based on modalias strings.
      32             :  * Uses a Patricia/radix trie to index all matches for efficient lookup.
      33             :  */
      34             : 
      35             : /* in-memory trie objects */
      36             : struct trie {
      37             :         struct trie_node *root;
      38             :         struct strbuf *strings;
      39             : 
      40             :         size_t nodes_count;
      41             :         size_t children_count;
      42             :         size_t values_count;
      43             : };
      44             : 
      45             : struct trie_node {
      46             :         /* prefix, common part for all children of this node */
      47             :         size_t prefix_off;
      48             : 
      49             :         /* sorted array of pointers to children nodes */
      50             :         struct trie_child_entry *children;
      51             :         uint8_t children_count;
      52             : 
      53             :         /* sorted array of key-value pairs */
      54             :         struct trie_value_entry *values;
      55             :         size_t values_count;
      56             : };
      57             : 
      58             : /* children array item with char (0-255) index */
      59             : struct trie_child_entry {
      60             :         uint8_t c;
      61             :         struct trie_node *child;
      62             : };
      63             : 
      64             : /* value array item with key-value pairs */
      65             : struct trie_value_entry {
      66             :         size_t key_off;
      67             :         size_t value_off;
      68             :         size_t filename_off;
      69             :         uint32_t line_number;
      70             :         uint16_t file_priority;
      71             : };
      72             : 
      73     1805074 : static int trie_children_cmp(const struct trie_child_entry *a, const struct trie_child_entry *b) {
      74     1805074 :         return CMP(a->c, b->c);
      75             : }
      76             : 
      77      118035 : static int node_add_child(struct trie *trie, struct trie_node *node, struct trie_node *node_child, uint8_t c) {
      78             :         struct trie_child_entry *child;
      79             : 
      80             :         /* extend array, add new entry, sort for bisection */
      81      118035 :         child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
      82      118035 :         if (!child)
      83           0 :                 return -ENOMEM;
      84             : 
      85      118035 :         node->children = child;
      86      118035 :         trie->children_count++;
      87      118035 :         node->children[node->children_count].c = c;
      88      118035 :         node->children[node->children_count].child = node_child;
      89      118035 :         node->children_count++;
      90      118035 :         typesafe_qsort(node->children, node->children_count, trie_children_cmp);
      91      118035 :         trie->nodes_count++;
      92             : 
      93      118035 :         return 0;
      94             : }
      95             : 
      96      623227 : static struct trie_node *node_lookup(const struct trie_node *node, uint8_t c) {
      97             :         struct trie_child_entry *child;
      98             :         struct trie_child_entry search;
      99             : 
     100      623227 :         search.c = c;
     101      623227 :         child = typesafe_bsearch(&search, node->children, node->children_count, trie_children_cmp);
     102      623227 :         if (child)
     103      534647 :                 return child->child;
     104       88580 :         return NULL;
     105             : }
     106             : 
     107      118037 : static void trie_node_cleanup(struct trie_node *node) {
     108             :         size_t i;
     109             : 
     110      118037 :         if (!node)
     111           0 :                 return;
     112             : 
     113      236072 :         for (i = 0; i < node->children_count; i++)
     114      118035 :                 trie_node_cleanup(node->children[i].child);
     115      118037 :         free(node->children);
     116      118037 :         free(node->values);
     117      118037 :         free(node);
     118             : }
     119             : 
     120           2 : static void trie_free(struct trie *trie) {
     121           2 :         if (!trie)
     122           0 :                 return;
     123             : 
     124           2 :         trie_node_cleanup(trie->root);
     125           2 :         strbuf_cleanup(trie->strings);
     126           2 :         free(trie);
     127             : }
     128             : 
     129           2 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free);
     130             : 
     131       22226 : static int trie_values_cmp(const struct trie_value_entry *a, const struct trie_value_entry *b, struct trie *trie) {
     132       44452 :         return strcmp(trie->strings->buf + a->key_off,
     133       22226 :                       trie->strings->buf + b->key_off);
     134             : }
     135             : 
     136       89818 : static int trie_node_add_value(struct trie *trie, struct trie_node *node,
     137             :                                const char *key, const char *value,
     138             :                                const char *filename, uint16_t file_priority, uint32_t line_number, bool compat) {
     139       89818 :         ssize_t k, v, fn = 0;
     140             :         struct trie_value_entry *val;
     141             : 
     142       89818 :         k = strbuf_add_string(trie->strings, key, strlen(key));
     143       89818 :         if (k < 0)
     144           0 :                 return k;
     145       89818 :         v = strbuf_add_string(trie->strings, value, strlen(value));
     146       89818 :         if (v < 0)
     147           0 :                 return v;
     148             : 
     149       89818 :         if (!compat) {
     150       89818 :                 fn = strbuf_add_string(trie->strings, filename, strlen(filename));
     151       89818 :                 if (fn < 0)
     152           0 :                         return fn;
     153             :         }
     154             : 
     155       89818 :         if (node->values_count) {
     156        1238 :                 struct trie_value_entry search = {
     157             :                         .key_off = k,
     158             :                         .value_off = v,
     159             :                 };
     160             : 
     161        1238 :                 val = typesafe_bsearch_r(&search, node->values, node->values_count, trie_values_cmp, trie);
     162        1238 :                 if (val) {
     163             :                         /* At this point we have 2 identical properties on the same match-string.
     164             :                          * Since we process files in order, we just replace the previous value. */
     165           2 :                         val->value_off = v;
     166           2 :                         val->filename_off = fn;
     167           2 :                         val->file_priority = file_priority;
     168           2 :                         val->line_number = line_number;
     169           2 :                         return 0;
     170             :                 }
     171             :         }
     172             : 
     173             :         /* extend array, add new entry, sort for bisection */
     174       89816 :         val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
     175       89816 :         if (!val)
     176           0 :                 return -ENOMEM;
     177       89816 :         trie->values_count++;
     178       89816 :         node->values = val;
     179       89816 :         node->values[node->values_count] = (struct trie_value_entry) {
     180             :                 .key_off = k,
     181             :                 .value_off = v,
     182             :                 .filename_off = fn,
     183             :                 .file_priority = file_priority,
     184             :                 .line_number = line_number,
     185             :         };
     186       89816 :         node->values_count++;
     187       89816 :         typesafe_qsort_r(node->values, node->values_count, trie_values_cmp, trie);
     188       89816 :         return 0;
     189             : }
     190             : 
     191       89818 : static int trie_insert(struct trie *trie, struct trie_node *node, const char *search,
     192             :                        const char *key, const char *value,
     193             :                        const char *filename, uint16_t file_priority, uint32_t line_number, bool compat) {
     194       89818 :         size_t i = 0;
     195       89818 :         int r = 0;
     196             : 
     197      534647 :         for (;;) {
     198             :                 size_t p;
     199             :                 uint8_t c;
     200             :                 struct trie_node *child;
     201             : 
     202     1520727 :                 for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
     203      925717 :                         _cleanup_free_ struct trie_node *new_child = NULL;
     204      925717 :                         _cleanup_free_ char *s = NULL;
     205             :                         ssize_t off;
     206             : 
     207      925717 :                         if (c == search[i + p])
     208      896262 :                                 continue;
     209             : 
     210             :                         /* split node */
     211       29455 :                         new_child = new(struct trie_node, 1);
     212       29455 :                         if (!new_child)
     213           0 :                                 return -ENOMEM;
     214             : 
     215             :                         /* move values from parent to child */
     216       58910 :                         *new_child = (struct trie_node) {
     217       29455 :                                 .prefix_off = node->prefix_off + p+1,
     218       29455 :                                 .children = node->children,
     219       29455 :                                 .children_count = node->children_count,
     220       29455 :                                 .values = node->values,
     221       29455 :                                 .values_count = node->values_count,
     222             :                         };
     223             : 
     224             :                         /* update parent; use strdup() because the source gets realloc()d */
     225       29455 :                         s = strndup(trie->strings->buf + node->prefix_off, p);
     226       29455 :                         if (!s)
     227           0 :                                 return -ENOMEM;
     228             : 
     229       29455 :                         off = strbuf_add_string(trie->strings, s, p);
     230       29455 :                         if (off < 0)
     231           0 :                                 return off;
     232             : 
     233       29455 :                         *node = (struct trie_node) {
     234             :                                 .prefix_off = off,
     235             :                         };
     236       29455 :                         r = node_add_child(trie, node, new_child, c);
     237       29455 :                         if (r < 0)
     238           0 :                                 return r;
     239             : 
     240       29455 :                         new_child = NULL; /* avoid cleanup */
     241       29455 :                         break;
     242             :                 }
     243      624465 :                 i += p;
     244             : 
     245      624465 :                 c = search[i];
     246      624465 :                 if (c == '\0')
     247        1238 :                         return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number, compat);
     248             : 
     249      623227 :                 child = node_lookup(node, c);
     250      623227 :                 if (!child) {
     251       88580 :                         _cleanup_free_ struct trie_node *new_child = NULL;
     252             :                         ssize_t off;
     253             : 
     254             :                         /* new child */
     255       88580 :                         new_child = new(struct trie_node, 1);
     256       88580 :                         if (!new_child)
     257           0 :                                 return -ENOMEM;
     258             : 
     259       88580 :                         off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
     260       88580 :                         if (off < 0)
     261           0 :                                 return off;
     262             : 
     263       88580 :                         *new_child = (struct trie_node) {
     264             :                                 .prefix_off = off,
     265             :                         };
     266             : 
     267       88580 :                         r = node_add_child(trie, node, new_child, c);
     268       88580 :                         if (r < 0)
     269           0 :                                 return r;
     270             : 
     271       88580 :                         child = TAKE_PTR(new_child);
     272       88580 :                         return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number, compat);
     273             :                 }
     274             : 
     275      534647 :                 node = child;
     276      534647 :                 i++;
     277             :         }
     278             : }
     279             : 
     280             : struct trie_f {
     281             :         FILE *f;
     282             :         struct trie *trie;
     283             :         uint64_t strings_off;
     284             : 
     285             :         uint64_t nodes_count;
     286             :         uint64_t children_count;
     287             :         uint64_t values_count;
     288             : };
     289             : 
     290             : /* calculate the storage space for the nodes, children arrays, value arrays */
     291      118037 : static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node, bool compat) {
     292             :         uint64_t i;
     293             : 
     294      236072 :         for (i = 0; i < node->children_count; i++)
     295      118035 :                 trie_store_nodes_size(trie, node->children[i].child, compat);
     296             : 
     297      118037 :         trie->strings_off += sizeof(struct trie_node_f);
     298      236072 :         for (i = 0; i < node->children_count; i++)
     299      118035 :                 trie->strings_off += sizeof(struct trie_child_entry_f);
     300      207853 :         for (i = 0; i < node->values_count; i++)
     301       89816 :                 trie->strings_off += compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f);
     302      118037 : }
     303             : 
     304      118037 : static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node, bool compat) {
     305             :         uint64_t i;
     306      472148 :         struct trie_node_f n = {
     307      118037 :                 .prefix_off = htole64(trie->strings_off + node->prefix_off),
     308      118037 :                 .children_count = node->children_count,
     309      118037 :                 .values_count = htole64(node->values_count),
     310             :         };
     311      118037 :         _cleanup_free_ struct trie_child_entry_f *children = NULL;
     312             :         int64_t node_off;
     313             : 
     314      118037 :         if (node->children_count) {
     315       29465 :                 children = new(struct trie_child_entry_f, node->children_count);
     316       29465 :                 if (!children)
     317           0 :                         return -ENOMEM;
     318             :         }
     319             : 
     320             :         /* post-order recursion */
     321      236072 :         for (i = 0; i < node->children_count; i++) {
     322             :                 int64_t child_off;
     323             : 
     324      118035 :                 child_off = trie_store_nodes(trie, node->children[i].child, compat);
     325      118035 :                 if (child_off < 0)
     326           0 :                         return child_off;
     327             : 
     328      118035 :                 children[i] = (struct trie_child_entry_f) {
     329      118035 :                         .c = node->children[i].c,
     330      118035 :                         .child_off = htole64(child_off),
     331             :                 };
     332             :         }
     333             : 
     334             :         /* write node */
     335      118037 :         node_off = ftello(trie->f);
     336      118037 :         fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
     337      118037 :         trie->nodes_count++;
     338             : 
     339             :         /* append children array */
     340      118037 :         if (node->children_count) {
     341       29465 :                 fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
     342       29465 :                 trie->children_count += node->children_count;
     343             :         }
     344             : 
     345             :         /* append values array */
     346      207853 :         for (i = 0; i < node->values_count; i++) {
     347      538896 :                 struct trie_value_entry2_f v = {
     348       89816 :                         .key_off = htole64(trie->strings_off + node->values[i].key_off),
     349       89816 :                         .value_off = htole64(trie->strings_off + node->values[i].value_off),
     350       89816 :                         .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
     351       89816 :                         .line_number = htole32(node->values[i].line_number),
     352       89816 :                         .file_priority = htole16(node->values[i].file_priority),
     353             :                 };
     354             : 
     355       89816 :                 fwrite(&v, compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f), 1, trie->f);
     356             :         }
     357      118037 :         trie->values_count += node->values_count;
     358             : 
     359      118037 :         return node_off;
     360             : }
     361             : 
     362           2 : static int trie_store(struct trie *trie, const char *filename, bool compat) {
     363           2 :         struct trie_f t = {
     364             :                 .trie = trie,
     365             :         };
     366           2 :         _cleanup_free_ char *filename_tmp = NULL;
     367             :         int64_t pos;
     368             :         int64_t root_off;
     369             :         int64_t size;
     370          12 :         struct trie_header_f h = {
     371             :                 .signature = HWDB_SIG,
     372           2 :                 .tool_version = htole64(PROJECT_VERSION),
     373           2 :                 .header_size = htole64(sizeof(struct trie_header_f)),
     374           2 :                 .node_size = htole64(sizeof(struct trie_node_f)),
     375           2 :                 .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
     376           2 :                 .value_entry_size = htole64(compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f)),
     377             :         };
     378             :         int r;
     379             : 
     380             :         /* calculate size of header, nodes, children entries, value entries */
     381           2 :         t.strings_off = sizeof(struct trie_header_f);
     382           2 :         trie_store_nodes_size(&t, trie->root, compat);
     383             : 
     384           2 :         r = fopen_temporary(filename, &t.f, &filename_tmp);
     385           2 :         if (r < 0)
     386           0 :                 return r;
     387           2 :         fchmod(fileno(t.f), 0444);
     388             : 
     389             :         /* write nodes */
     390           2 :         if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
     391           0 :                 goto error_fclose;
     392             : 
     393           2 :         root_off = trie_store_nodes(&t, trie->root, compat);
     394           2 :         h.nodes_root_off = htole64(root_off);
     395           2 :         pos = ftello(t.f);
     396           2 :         h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
     397             : 
     398             :         /* write string buffer */
     399           2 :         fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
     400           2 :         h.strings_len = htole64(trie->strings->len);
     401             : 
     402             :         /* write header */
     403           2 :         size = ftello(t.f);
     404           2 :         h.file_size = htole64(size);
     405           2 :         if (fseeko(t.f, 0, SEEK_SET) < 0)
     406           0 :                 goto error_fclose;
     407           2 :         fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
     408             : 
     409           2 :         if (ferror(t.f))
     410           0 :                 goto error_fclose;
     411           2 :         if (fflush(t.f) < 0)
     412           0 :                 goto error_fclose;
     413           2 :         if (fsync(fileno(t.f)) < 0)
     414           0 :                 goto error_fclose;
     415           2 :         if (rename(filename_tmp, filename) < 0)
     416           0 :                 goto error_fclose;
     417             : 
     418             :         /* write succeeded */
     419           2 :         fclose(t.f);
     420             : 
     421           2 :         log_debug("=== trie on-disk ===");
     422           2 :         log_debug("size:             %8"PRIi64" bytes", size);
     423           2 :         log_debug("header:           %8zu bytes", sizeof(struct trie_header_f));
     424           2 :         log_debug("nodes:            %8"PRIu64" bytes (%8"PRIu64")",
     425             :                   t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
     426           2 :         log_debug("child pointers:   %8"PRIu64" bytes (%8"PRIu64")",
     427             :                   t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
     428           2 :         log_debug("value pointers:   %8"PRIu64" bytes (%8"PRIu64")",
     429             :                   t.values_count * (compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f)), t.values_count);
     430           2 :         log_debug("string store:     %8zu bytes", trie->strings->len);
     431           2 :         log_debug("strings start:    %8"PRIu64, t.strings_off);
     432           2 :         return 0;
     433             : 
     434           0 :  error_fclose:
     435           0 :         r = -errno;
     436           0 :         fclose(t.f);
     437           0 :         (void) unlink(filename_tmp);
     438           0 :         return r;
     439             : }
     440             : 
     441       89339 : static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename,
     442             :                        uint16_t file_priority, uint32_t line_number, bool compat) {
     443             :         char *value, **entry;
     444             : 
     445       89339 :         assert(line[0] == ' ');
     446             : 
     447       89339 :         value = strchr(line, '=');
     448       89339 :         if (!value)
     449           1 :                 return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     450             :                                   "Key-value pair expected but got \"%s\", ignoring", line);
     451             : 
     452       89338 :         value[0] = '\0';
     453       89338 :         value++;
     454             : 
     455             :         /* Replace multiple leading spaces by a single space */
     456       89339 :         while (isblank(line[0]) && isblank(line[1]))
     457           1 :                 line++;
     458             : 
     459       89338 :         if (isempty(line + 1) || isempty(value))
     460           2 :                 return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     461             :                                   "Empty %s in \"%s=%s\", ignoring",
     462             :                                   isempty(line + 1) ? "key" : "value",
     463             :                                   line, value);
     464             : 
     465      179154 :         STRV_FOREACH(entry, match_list)
     466       89818 :                 trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number, compat);
     467             : 
     468       89336 :         return 0;
     469             : }
     470             : 
     471          20 : static int import_file(struct trie *trie, const char *filename, uint16_t file_priority, bool compat) {
     472             :         enum {
     473             :                 HW_NONE,
     474             :                 HW_MATCH,
     475             :                 HW_DATA,
     476          20 :         } state = HW_NONE;
     477          20 :         _cleanup_fclose_ FILE *f = NULL;
     478          20 :         _cleanup_strv_free_ char **match_list = NULL;
     479          20 :         uint32_t line_number = 0;
     480          20 :         char *match = NULL;
     481          20 :         int r = 0, err;
     482             : 
     483          20 :         f = fopen(filename, "re");
     484          20 :         if (!f)
     485           0 :                 return -errno;
     486             : 
     487      267912 :         for (;;) {
     488      267932 :                 _cleanup_free_ char *line = NULL;
     489             :                 size_t len;
     490             :                 char *pos;
     491             : 
     492      267932 :                 r = read_line(f, LONG_LINE_MAX, &line);
     493      267932 :                 if (r < 0)
     494           0 :                         return r;
     495      267932 :                 if (r == 0)
     496          20 :                         break;
     497             : 
     498      267912 :                 ++line_number;
     499             : 
     500             :                 /* comment line */
     501      267912 :                 if (line[0] == '#')
     502        1424 :                         continue;
     503             : 
     504             :                 /* strip trailing comment */
     505      266488 :                 pos = strchr(line, '#');
     506      266488 :                 if (pos)
     507        1637 :                         pos[0] = '\0';
     508             : 
     509             :                 /* strip trailing whitespace */
     510      266488 :                 len = strlen(line);
     511      280168 :                 while (len > 0 && isspace(line[len-1]))
     512       13680 :                         len--;
     513      266488 :                 line[len] = '\0';
     514             : 
     515      266488 :                 switch (state) {
     516       88578 :                 case HW_NONE:
     517       88578 :                         if (len == 0)
     518         139 :                                 break;
     519             : 
     520       88439 :                         if (line[0] == ' ') {
     521           1 :                                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     522             :                                            "Match expected but got indented property \"%s\", ignoring line", line);
     523           1 :                                 r = -EINVAL;
     524           1 :                                 break;
     525             :                         }
     526             : 
     527             :                         /* start of record, first match */
     528       88438 :                         state = HW_MATCH;
     529             : 
     530       88438 :                         match = strdup(line);
     531       88438 :                         if (!match)
     532           0 :                                 return -ENOMEM;
     533             : 
     534       88438 :                         err = strv_consume(&match_list, match);
     535       88438 :                         if (err < 0)
     536           0 :                                 return err;
     537             : 
     538       88438 :                         break;
     539             : 
     540       88590 :                 case HW_MATCH:
     541       88590 :                         if (len == 0) {
     542           3 :                                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     543             :                                            "Property expected, ignoring record with no properties");
     544           3 :                                 r = -EINVAL;
     545           3 :                                 state = HW_NONE;
     546           3 :                                 strv_clear(match_list);
     547           3 :                                 break;
     548             :                         }
     549             : 
     550       88587 :                         if (line[0] != ' ') {
     551             :                                 /* another match */
     552         153 :                                 match = strdup(line);
     553         153 :                                 if (!match)
     554           0 :                                         return -ENOMEM;
     555             : 
     556         153 :                                 err = strv_consume(&match_list, match);
     557         153 :                                 if (err < 0)
     558           0 :                                         return err;
     559             : 
     560         153 :                                 break;
     561             :                         }
     562             : 
     563             :                         /* first data */
     564       88434 :                         state = HW_DATA;
     565       88434 :                         err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
     566       88434 :                         if (err < 0)
     567           2 :                                 r = err;
     568       88434 :                         break;
     569             : 
     570       89320 :                 case HW_DATA:
     571       89320 :                         if (len == 0) {
     572             :                                 /* end of record */
     573       88413 :                                 state = HW_NONE;
     574       88413 :                                 strv_clear(match_list);
     575       88413 :                                 break;
     576             :                         }
     577             : 
     578         907 :                         if (line[0] != ' ') {
     579           2 :                                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     580             :                                            "Property or empty line expected, got \"%s\", ignoring record", line);
     581           2 :                                 r = -EINVAL;
     582           2 :                                 state = HW_NONE;
     583           2 :                                 strv_clear(match_list);
     584           2 :                                 break;
     585             :                         }
     586             : 
     587         905 :                         err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
     588         905 :                         if (err < 0)
     589           1 :                                 r = err;
     590         905 :                         break;
     591      266488 :                 };
     592             :         }
     593             : 
     594          20 :         if (state == HW_MATCH)
     595           1 :                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     596             :                            "Property expected, ignoring record with no properties");
     597             : 
     598          20 :         return r;
     599             : }
     600             : 
     601           2 : int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat) {
     602           2 :         _cleanup_free_ char *hwdb_bin = NULL;
     603           2 :         _cleanup_(trie_freep) struct trie *trie = NULL;
     604           2 :         _cleanup_strv_free_ char **files = NULL;
     605             :         char **f;
     606           2 :         uint16_t file_priority = 1;
     607           2 :         int r = 0, err;
     608             : 
     609             :         /* The argument 'compat' controls the format version of database. If false, then hwdb.bin will be created with
     610             :          * additional information such that priority, line number, and filename of database source. If true, then hwdb.bin
     611             :          * will be created without the information. systemd-hwdb command should set the argument false, and 'udevadm hwdb'
     612             :          * command should set it true. */
     613             : 
     614           2 :         trie = new0(struct trie, 1);
     615           2 :         if (!trie)
     616           0 :                 return -ENOMEM;
     617             : 
     618             :         /* string store */
     619           2 :         trie->strings = strbuf_new();
     620           2 :         if (!trie->strings)
     621           0 :                 return -ENOMEM;
     622             : 
     623             :         /* index */
     624           2 :         trie->root = new0(struct trie_node, 1);
     625           2 :         if (!trie->root)
     626           0 :                 return -ENOMEM;
     627             : 
     628           2 :         trie->nodes_count++;
     629             : 
     630           2 :         err = conf_files_list_strv(&files, ".hwdb", root, 0, conf_file_dirs);
     631           2 :         if (err < 0)
     632           0 :                 return log_error_errno(err, "Failed to enumerate hwdb files: %m");
     633             : 
     634          22 :         STRV_FOREACH(f, files) {
     635          20 :                 log_debug("Reading file \"%s\"", *f);
     636          20 :                 err = import_file(trie, *f, file_priority++, compat);
     637          20 :                 if (err < 0 && strict)
     638           0 :                         r = err;
     639             :         }
     640             : 
     641           2 :         strbuf_complete(trie->strings);
     642             : 
     643           2 :         log_debug("=== trie in-memory ===");
     644           2 :         log_debug("nodes:            %8zu bytes (%8zu)",
     645             :                   trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
     646           2 :         log_debug("children arrays:  %8zu bytes (%8zu)",
     647             :                   trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
     648           2 :         log_debug("values arrays:    %8zu bytes (%8zu)",
     649             :                   trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
     650           2 :         log_debug("strings:          %8zu bytes",
     651             :                   trie->strings->len);
     652           2 :         log_debug("strings incoming: %8zu bytes (%8zu)",
     653             :                   trie->strings->in_len, trie->strings->in_count);
     654           2 :         log_debug("strings dedup'ed: %8zu bytes (%8zu)",
     655             :                   trie->strings->dedup_len, trie->strings->dedup_count);
     656             : 
     657           2 :         hwdb_bin = path_join(root, hwdb_bin_dir ?: default_hwdb_bin_dir, "hwdb.bin");
     658           2 :         if (!hwdb_bin)
     659           0 :                 return -ENOMEM;
     660             : 
     661           2 :         mkdir_parents_label(hwdb_bin, 0755);
     662           2 :         err = trie_store(trie, hwdb_bin, compat);
     663           2 :         if (err < 0)
     664           0 :                 return log_error_errno(err, "Failed to write database %s: %m", hwdb_bin);
     665             : 
     666           2 :         err = label_fix(hwdb_bin, 0);
     667           2 :         if (err < 0)
     668           0 :                 return err;
     669             : 
     670           2 :         return r;
     671             : }
     672             : 
     673           0 : int hwdb_query(const char *modalias) {
     674           0 :         _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
     675             :         const char *key, *value;
     676             :         int r;
     677             : 
     678           0 :         assert(modalias);
     679             : 
     680           0 :         r = sd_hwdb_new(&hwdb);
     681           0 :         if (r < 0)
     682           0 :                 return r;
     683             : 
     684           0 :         SD_HWDB_FOREACH_PROPERTY(hwdb, modalias, key, value)
     685           0 :                 printf("%s=%s\n", key, value);
     686             : 
     687           0 :         return 0;
     688             : }

Generated by: LCOV version 1.14