LCOV - code coverage report
Current view: top level - libsystemd/sd-hwdb - hwdb-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 309 360 85.8 %
Date: 2019-08-23 13:36:53 Functions: 15 16 93.8 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 153 242 63.2 %

           Branch data     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                 :    7220296 : static int trie_children_cmp(const struct trie_child_entry *a, const struct trie_child_entry *b) {
      74         [ +  + ]:    7220296 :         return CMP(a->c, b->c);
      75                 :            : }
      76                 :            : 
      77                 :     472140 : 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                 :     472140 :         child = reallocarray(node->children, node->children_count + 1, sizeof(struct trie_child_entry));
      82         [ -  + ]:     472140 :         if (!child)
      83                 :          0 :                 return -ENOMEM;
      84                 :            : 
      85                 :     472140 :         node->children = child;
      86                 :     472140 :         trie->children_count++;
      87                 :     472140 :         node->children[node->children_count].c = c;
      88                 :     472140 :         node->children[node->children_count].child = node_child;
      89                 :     472140 :         node->children_count++;
      90                 :     472140 :         typesafe_qsort(node->children, node->children_count, trie_children_cmp);
      91                 :     472140 :         trie->nodes_count++;
      92                 :            : 
      93                 :     472140 :         return 0;
      94                 :            : }
      95                 :            : 
      96                 :    2492908 : 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                 :    2492908 :         search.c = c;
     101                 :    2492908 :         child = typesafe_bsearch(&search, node->children, node->children_count, trie_children_cmp);
     102         [ +  + ]:    2492908 :         if (child)
     103                 :    2138588 :                 return child->child;
     104                 :     354320 :         return NULL;
     105                 :            : }
     106                 :            : 
     107                 :     472148 : static void trie_node_cleanup(struct trie_node *node) {
     108                 :            :         size_t i;
     109                 :            : 
     110         [ -  + ]:     472148 :         if (!node)
     111                 :          0 :                 return;
     112                 :            : 
     113         [ +  + ]:     944288 :         for (i = 0; i < node->children_count; i++)
     114                 :     472140 :                 trie_node_cleanup(node->children[i].child);
     115                 :     472148 :         free(node->children);
     116                 :     472148 :         free(node->values);
     117                 :     472148 :         free(node);
     118                 :            : }
     119                 :            : 
     120                 :          8 : static void trie_free(struct trie *trie) {
     121         [ -  + ]:          8 :         if (!trie)
     122                 :          0 :                 return;
     123                 :            : 
     124                 :          8 :         trie_node_cleanup(trie->root);
     125                 :          8 :         strbuf_cleanup(trie->strings);
     126                 :          8 :         free(trie);
     127                 :            : }
     128                 :            : 
     129         [ +  - ]:          8 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct trie*, trie_free);
     130                 :            : 
     131                 :      88904 : static int trie_values_cmp(const struct trie_value_entry *a, const struct trie_value_entry *b, struct trie *trie) {
     132                 :     177808 :         return strcmp(trie->strings->buf + a->key_off,
     133                 :      88904 :                       trie->strings->buf + b->key_off);
     134                 :            : }
     135                 :            : 
     136                 :     359272 : 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                 :     359272 :         ssize_t k, v, fn = 0;
     140                 :            :         struct trie_value_entry *val;
     141                 :            : 
     142                 :     359272 :         k = strbuf_add_string(trie->strings, key, strlen(key));
     143         [ -  + ]:     359272 :         if (k < 0)
     144                 :          0 :                 return k;
     145                 :     359272 :         v = strbuf_add_string(trie->strings, value, strlen(value));
     146         [ -  + ]:     359272 :         if (v < 0)
     147                 :          0 :                 return v;
     148                 :            : 
     149         [ +  - ]:     359272 :         if (!compat) {
     150                 :     359272 :                 fn = strbuf_add_string(trie->strings, filename, strlen(filename));
     151         [ -  + ]:     359272 :                 if (fn < 0)
     152                 :          0 :                         return fn;
     153                 :            :         }
     154                 :            : 
     155         [ +  + ]:     359272 :         if (node->values_count) {
     156                 :       4952 :                 struct trie_value_entry search = {
     157                 :            :                         .key_off = k,
     158                 :            :                         .value_off = v,
     159                 :            :                 };
     160                 :            : 
     161                 :       4952 :                 val = typesafe_bsearch_r(&search, node->values, node->values_count, trie_values_cmp, trie);
     162         [ +  + ]:       4952 :                 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                 :          8 :                         val->value_off = v;
     166                 :          8 :                         val->filename_off = fn;
     167                 :          8 :                         val->file_priority = file_priority;
     168                 :          8 :                         val->line_number = line_number;
     169                 :          8 :                         return 0;
     170                 :            :                 }
     171                 :            :         }
     172                 :            : 
     173                 :            :         /* extend array, add new entry, sort for bisection */
     174                 :     359264 :         val = reallocarray(node->values, node->values_count + 1, sizeof(struct trie_value_entry));
     175         [ -  + ]:     359264 :         if (!val)
     176                 :          0 :                 return -ENOMEM;
     177                 :     359264 :         trie->values_count++;
     178                 :     359264 :         node->values = val;
     179                 :     359264 :         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                 :     359264 :         node->values_count++;
     187                 :     359264 :         typesafe_qsort_r(node->values, node->values_count, trie_values_cmp, trie);
     188                 :     359264 :         return 0;
     189                 :            : }
     190                 :            : 
     191                 :     359272 : 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                 :     359272 :         size_t i = 0;
     195                 :     359272 :         int r = 0;
     196                 :            : 
     197                 :    2138588 :         for (;;) {
     198                 :            :                 size_t p;
     199                 :            :                 uint8_t c;
     200                 :            :                 struct trie_node *child;
     201                 :            : 
     202         [ +  + ]:    6082908 :                 for (p = 0; (c = trie->strings->buf[node->prefix_off + p]); p++) {
     203      [ +  -  + ]:    3702868 :                         _cleanup_free_ struct trie_node *new_child = NULL;
     204      [ +  -  + ]:    3702868 :                         _cleanup_free_ char *s = NULL;
     205                 :            :                         ssize_t off;
     206                 :            : 
     207         [ +  + ]:    3702868 :                         if (c == search[i + p])
     208                 :    3585048 :                                 continue;
     209                 :            : 
     210                 :            :                         /* split node */
     211                 :     117820 :                         new_child = new(struct trie_node, 1);
     212         [ -  + ]:     117820 :                         if (!new_child)
     213                 :          0 :                                 return -ENOMEM;
     214                 :            : 
     215                 :            :                         /* move values from parent to child */
     216                 :     235640 :                         *new_child = (struct trie_node) {
     217                 :     117820 :                                 .prefix_off = node->prefix_off + p+1,
     218                 :     117820 :                                 .children = node->children,
     219                 :     117820 :                                 .children_count = node->children_count,
     220                 :     117820 :                                 .values = node->values,
     221                 :     117820 :                                 .values_count = node->values_count,
     222                 :            :                         };
     223                 :            : 
     224                 :            :                         /* update parent; use strdup() because the source gets realloc()d */
     225                 :     117820 :                         s = strndup(trie->strings->buf + node->prefix_off, p);
     226         [ -  + ]:     117820 :                         if (!s)
     227                 :          0 :                                 return -ENOMEM;
     228                 :            : 
     229                 :     117820 :                         off = strbuf_add_string(trie->strings, s, p);
     230         [ -  + ]:     117820 :                         if (off < 0)
     231                 :          0 :                                 return off;
     232                 :            : 
     233                 :     117820 :                         *node = (struct trie_node) {
     234                 :            :                                 .prefix_off = off,
     235                 :            :                         };
     236                 :     117820 :                         r = node_add_child(trie, node, new_child, c);
     237         [ -  + ]:     117820 :                         if (r < 0)
     238                 :          0 :                                 return r;
     239                 :            : 
     240                 :     117820 :                         new_child = NULL; /* avoid cleanup */
     241                 :     117820 :                         break;
     242                 :            :                 }
     243                 :    2497860 :                 i += p;
     244                 :            : 
     245                 :    2497860 :                 c = search[i];
     246         [ +  + ]:    2497860 :                 if (c == '\0')
     247                 :       4952 :                         return trie_node_add_value(trie, node, key, value, filename, file_priority, line_number, compat);
     248                 :            : 
     249                 :    2492908 :                 child = node_lookup(node, c);
     250         [ +  + ]:    2492908 :                 if (!child) {
     251                 :     354320 :                         _cleanup_free_ struct trie_node *new_child = NULL;
     252                 :            :                         ssize_t off;
     253                 :            : 
     254                 :            :                         /* new child */
     255                 :     354320 :                         new_child = new(struct trie_node, 1);
     256         [ -  + ]:     354320 :                         if (!new_child)
     257                 :          0 :                                 return -ENOMEM;
     258                 :            : 
     259                 :     354320 :                         off = strbuf_add_string(trie->strings, search + i+1, strlen(search + i+1));
     260         [ -  + ]:     354320 :                         if (off < 0)
     261                 :          0 :                                 return off;
     262                 :            : 
     263                 :     354320 :                         *new_child = (struct trie_node) {
     264                 :            :                                 .prefix_off = off,
     265                 :            :                         };
     266                 :            : 
     267                 :     354320 :                         r = node_add_child(trie, node, new_child, c);
     268         [ -  + ]:     354320 :                         if (r < 0)
     269                 :          0 :                                 return r;
     270                 :            : 
     271                 :     354320 :                         child = TAKE_PTR(new_child);
     272                 :     354320 :                         return trie_node_add_value(trie, child, key, value, filename, file_priority, line_number, compat);
     273                 :            :                 }
     274                 :            : 
     275                 :    2138588 :                 node = child;
     276                 :    2138588 :                 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                 :     472148 : static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node, bool compat) {
     292                 :            :         uint64_t i;
     293                 :            : 
     294         [ +  + ]:     944288 :         for (i = 0; i < node->children_count; i++)
     295                 :     472140 :                 trie_store_nodes_size(trie, node->children[i].child, compat);
     296                 :            : 
     297                 :     472148 :         trie->strings_off += sizeof(struct trie_node_f);
     298         [ +  + ]:     944288 :         for (i = 0; i < node->children_count; i++)
     299                 :     472140 :                 trie->strings_off += sizeof(struct trie_child_entry_f);
     300         [ +  + ]:     831412 :         for (i = 0; i < node->values_count; i++)
     301         [ -  + ]:     359264 :                 trie->strings_off += compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f);
     302                 :     472148 : }
     303                 :            : 
     304                 :     472148 : static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node, bool compat) {
     305                 :            :         uint64_t i;
     306                 :    1888592 :         struct trie_node_f n = {
     307                 :     472148 :                 .prefix_off = htole64(trie->strings_off + node->prefix_off),
     308                 :     472148 :                 .children_count = node->children_count,
     309                 :     472148 :                 .values_count = htole64(node->values_count),
     310                 :            :         };
     311                 :     472148 :         _cleanup_free_ struct trie_child_entry_f *children = NULL;
     312                 :            :         int64_t node_off;
     313                 :            : 
     314         [ +  + ]:     472148 :         if (node->children_count) {
     315                 :     117860 :                 children = new(struct trie_child_entry_f, node->children_count);
     316         [ -  + ]:     117860 :                 if (!children)
     317                 :          0 :                         return -ENOMEM;
     318                 :            :         }
     319                 :            : 
     320                 :            :         /* post-order recursion */
     321         [ +  + ]:     944288 :         for (i = 0; i < node->children_count; i++) {
     322                 :            :                 int64_t child_off;
     323                 :            : 
     324                 :     472140 :                 child_off = trie_store_nodes(trie, node->children[i].child, compat);
     325         [ -  + ]:     472140 :                 if (child_off < 0)
     326                 :          0 :                         return child_off;
     327                 :            : 
     328                 :     472140 :                 children[i] = (struct trie_child_entry_f) {
     329                 :     472140 :                         .c = node->children[i].c,
     330                 :     472140 :                         .child_off = htole64(child_off),
     331                 :            :                 };
     332                 :            :         }
     333                 :            : 
     334                 :            :         /* write node */
     335                 :     472148 :         node_off = ftello(trie->f);
     336                 :     472148 :         fwrite(&n, sizeof(struct trie_node_f), 1, trie->f);
     337                 :     472148 :         trie->nodes_count++;
     338                 :            : 
     339                 :            :         /* append children array */
     340         [ +  + ]:     472148 :         if (node->children_count) {
     341                 :     117860 :                 fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f);
     342                 :     117860 :                 trie->children_count += node->children_count;
     343                 :            :         }
     344                 :            : 
     345                 :            :         /* append values array */
     346         [ +  + ]:     831412 :         for (i = 0; i < node->values_count; i++) {
     347                 :    2155584 :                 struct trie_value_entry2_f v = {
     348                 :     359264 :                         .key_off = htole64(trie->strings_off + node->values[i].key_off),
     349                 :     359264 :                         .value_off = htole64(trie->strings_off + node->values[i].value_off),
     350                 :     359264 :                         .filename_off = htole64(trie->strings_off + node->values[i].filename_off),
     351                 :     359264 :                         .line_number = htole32(node->values[i].line_number),
     352                 :     359264 :                         .file_priority = htole16(node->values[i].file_priority),
     353                 :            :                 };
     354                 :            : 
     355         [ -  + ]:     359264 :                 fwrite(&v, compat ? sizeof(struct trie_value_entry_f) : sizeof(struct trie_value_entry2_f), 1, trie->f);
     356                 :            :         }
     357                 :     472148 :         trie->values_count += node->values_count;
     358                 :            : 
     359                 :     472148 :         return node_off;
     360                 :            : }
     361                 :            : 
     362                 :          8 : static int trie_store(struct trie *trie, const char *filename, bool compat) {
     363                 :          8 :         struct trie_f t = {
     364                 :            :                 .trie = trie,
     365                 :            :         };
     366                 :          8 :         _cleanup_free_ char *filename_tmp = NULL;
     367                 :            :         int64_t pos;
     368                 :            :         int64_t root_off;
     369                 :            :         int64_t size;
     370                 :         48 :         struct trie_header_f h = {
     371                 :            :                 .signature = HWDB_SIG,
     372                 :          8 :                 .tool_version = htole64(PROJECT_VERSION),
     373                 :          8 :                 .header_size = htole64(sizeof(struct trie_header_f)),
     374                 :          8 :                 .node_size = htole64(sizeof(struct trie_node_f)),
     375                 :          8 :                 .child_entry_size = htole64(sizeof(struct trie_child_entry_f)),
     376         [ -  + ]:          8 :                 .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                 :          8 :         t.strings_off = sizeof(struct trie_header_f);
     382                 :          8 :         trie_store_nodes_size(&t, trie->root, compat);
     383                 :            : 
     384                 :          8 :         r = fopen_temporary(filename, &t.f, &filename_tmp);
     385         [ -  + ]:          8 :         if (r < 0)
     386                 :          0 :                 return r;
     387                 :          8 :         fchmod(fileno(t.f), 0444);
     388                 :            : 
     389                 :            :         /* write nodes */
     390         [ -  + ]:          8 :         if (fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET) < 0)
     391                 :          0 :                 goto error_fclose;
     392                 :            : 
     393                 :          8 :         root_off = trie_store_nodes(&t, trie->root, compat);
     394                 :          8 :         h.nodes_root_off = htole64(root_off);
     395                 :          8 :         pos = ftello(t.f);
     396                 :          8 :         h.nodes_len = htole64(pos - sizeof(struct trie_header_f));
     397                 :            : 
     398                 :            :         /* write string buffer */
     399                 :          8 :         fwrite(trie->strings->buf, trie->strings->len, 1, t.f);
     400                 :          8 :         h.strings_len = htole64(trie->strings->len);
     401                 :            : 
     402                 :            :         /* write header */
     403                 :          8 :         size = ftello(t.f);
     404                 :          8 :         h.file_size = htole64(size);
     405         [ -  + ]:          8 :         if (fseeko(t.f, 0, SEEK_SET) < 0)
     406                 :          0 :                 goto error_fclose;
     407                 :          8 :         fwrite(&h, sizeof(struct trie_header_f), 1, t.f);
     408                 :            : 
     409         [ -  + ]:          8 :         if (ferror(t.f))
     410                 :          0 :                 goto error_fclose;
     411         [ -  + ]:          8 :         if (fflush(t.f) < 0)
     412                 :          0 :                 goto error_fclose;
     413         [ -  + ]:          8 :         if (fsync(fileno(t.f)) < 0)
     414                 :          0 :                 goto error_fclose;
     415         [ -  + ]:          8 :         if (rename(filename_tmp, filename) < 0)
     416                 :          0 :                 goto error_fclose;
     417                 :            : 
     418                 :            :         /* write succeeded */
     419                 :          8 :         fclose(t.f);
     420                 :            : 
     421         [ -  + ]:          8 :         log_debug("=== trie on-disk ===");
     422         [ -  + ]:          8 :         log_debug("size:             %8"PRIi64" bytes", size);
     423         [ -  + ]:          8 :         log_debug("header:           %8zu bytes", sizeof(struct trie_header_f));
     424         [ -  + ]:          8 :         log_debug("nodes:            %8"PRIu64" bytes (%8"PRIu64")",
     425                 :            :                   t.nodes_count * sizeof(struct trie_node_f), t.nodes_count);
     426         [ -  + ]:          8 :         log_debug("child pointers:   %8"PRIu64" bytes (%8"PRIu64")",
     427                 :            :                   t.children_count * sizeof(struct trie_child_entry_f), t.children_count);
     428   [ -  +  #  # ]:          8 :         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         [ -  + ]:          8 :         log_debug("string store:     %8zu bytes", trie->strings->len);
     431         [ -  + ]:          8 :         log_debug("strings start:    %8"PRIu64, t.strings_off);
     432                 :          8 :         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                 :     357356 : 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         [ -  + ]:     357356 :         assert(line[0] == ' ');
     446                 :            : 
     447                 :     357356 :         value = strchr(line, '=');
     448         [ +  + ]:     357356 :         if (!value)
     449         [ +  - ]:          4 :                 return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     450                 :            :                                   "Key-value pair expected but got \"%s\", ignoring", line);
     451                 :            : 
     452                 :     357352 :         value[0] = '\0';
     453                 :     357352 :         value++;
     454                 :            : 
     455                 :            :         /* Replace multiple leading spaces by a single space */
     456   [ +  -  +  + ]:     357356 :         while (isblank(line[0]) && isblank(line[1]))
     457                 :          4 :                 line++;
     458                 :            : 
     459   [ +  +  +  + ]:     357352 :         if (isempty(line + 1) || isempty(value))
     460   [ +  -  +  + ]:          8 :                 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   [ +  -  +  + ]:     716616 :         STRV_FOREACH(entry, match_list)
     466                 :     359272 :                 trie_insert(trie, trie->root, *entry, line, value, filename, file_priority, line_number, compat);
     467                 :            : 
     468                 :     357344 :         return 0;
     469                 :            : }
     470                 :            : 
     471                 :         80 : 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                 :         80 :         } state = HW_NONE;
     477                 :         80 :         _cleanup_fclose_ FILE *f = NULL;
     478                 :         80 :         _cleanup_strv_free_ char **match_list = NULL;
     479                 :         80 :         uint32_t line_number = 0;
     480                 :         80 :         char *match = NULL;
     481                 :         80 :         int r = 0, err;
     482                 :            : 
     483                 :         80 :         f = fopen(filename, "re");
     484         [ -  + ]:         80 :         if (!f)
     485                 :          0 :                 return -errno;
     486                 :            : 
     487                 :    1071648 :         for (;;) {
     488   [ +  -  +  + ]:    1071728 :                 _cleanup_free_ char *line = NULL;
     489                 :            :                 size_t len;
     490                 :            :                 char *pos;
     491                 :            : 
     492                 :    1071728 :                 r = read_line(f, LONG_LINE_MAX, &line);
     493         [ -  + ]:    1071728 :                 if (r < 0)
     494                 :          0 :                         return r;
     495         [ +  + ]:    1071728 :                 if (r == 0)
     496                 :         80 :                         break;
     497                 :            : 
     498                 :    1071648 :                 ++line_number;
     499                 :            : 
     500                 :            :                 /* comment line */
     501         [ +  + ]:    1071648 :                 if (line[0] == '#')
     502                 :       5696 :                         continue;
     503                 :            : 
     504                 :            :                 /* strip trailing comment */
     505                 :    1065952 :                 pos = strchr(line, '#');
     506         [ +  + ]:    1065952 :                 if (pos)
     507                 :       6548 :                         pos[0] = '\0';
     508                 :            : 
     509                 :            :                 /* strip trailing whitespace */
     510                 :    1065952 :                 len = strlen(line);
     511   [ +  +  +  + ]:    1120672 :                 while (len > 0 && isspace(line[len-1]))
     512                 :      54720 :                         len--;
     513                 :    1065952 :                 line[len] = '\0';
     514                 :            : 
     515   [ +  +  +  - ]:    1065952 :                 switch (state) {
     516                 :     354312 :                 case HW_NONE:
     517         [ +  + ]:     354312 :                         if (len == 0)
     518                 :        556 :                                 break;
     519                 :            : 
     520         [ +  + ]:     353756 :                         if (line[0] == ' ') {
     521         [ +  - ]:          4 :                                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     522                 :            :                                            "Match expected but got indented property \"%s\", ignoring line", line);
     523                 :          4 :                                 r = -EINVAL;
     524                 :          4 :                                 break;
     525                 :            :                         }
     526                 :            : 
     527                 :            :                         /* start of record, first match */
     528                 :     353752 :                         state = HW_MATCH;
     529                 :            : 
     530                 :     353752 :                         match = strdup(line);
     531         [ -  + ]:     353752 :                         if (!match)
     532                 :          0 :                                 return -ENOMEM;
     533                 :            : 
     534                 :     353752 :                         err = strv_consume(&match_list, match);
     535         [ -  + ]:     353752 :                         if (err < 0)
     536                 :          0 :                                 return err;
     537                 :            : 
     538                 :     353752 :                         break;
     539                 :            : 
     540                 :     354360 :                 case HW_MATCH:
     541         [ +  + ]:     354360 :                         if (len == 0) {
     542         [ +  - ]:         12 :                                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     543                 :            :                                            "Property expected, ignoring record with no properties");
     544                 :         12 :                                 r = -EINVAL;
     545                 :         12 :                                 state = HW_NONE;
     546                 :         12 :                                 strv_clear(match_list);
     547                 :         12 :                                 break;
     548                 :            :                         }
     549                 :            : 
     550         [ +  + ]:     354348 :                         if (line[0] != ' ') {
     551                 :            :                                 /* another match */
     552                 :        612 :                                 match = strdup(line);
     553         [ -  + ]:        612 :                                 if (!match)
     554                 :          0 :                                         return -ENOMEM;
     555                 :            : 
     556                 :        612 :                                 err = strv_consume(&match_list, match);
     557         [ -  + ]:        612 :                                 if (err < 0)
     558                 :          0 :                                         return err;
     559                 :            : 
     560                 :        612 :                                 break;
     561                 :            :                         }
     562                 :            : 
     563                 :            :                         /* first data */
     564                 :     353736 :                         state = HW_DATA;
     565                 :     353736 :                         err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
     566         [ +  + ]:     353736 :                         if (err < 0)
     567                 :          8 :                                 r = err;
     568                 :     353736 :                         break;
     569                 :            : 
     570                 :     357280 :                 case HW_DATA:
     571         [ +  + ]:     357280 :                         if (len == 0) {
     572                 :            :                                 /* end of record */
     573                 :     353652 :                                 state = HW_NONE;
     574                 :     353652 :                                 strv_clear(match_list);
     575                 :     353652 :                                 break;
     576                 :            :                         }
     577                 :            : 
     578         [ +  + ]:       3628 :                         if (line[0] != ' ') {
     579         [ +  - ]:          8 :                                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     580                 :            :                                            "Property or empty line expected, got \"%s\", ignoring record", line);
     581                 :          8 :                                 r = -EINVAL;
     582                 :          8 :                                 state = HW_NONE;
     583                 :          8 :                                 strv_clear(match_list);
     584                 :          8 :                                 break;
     585                 :            :                         }
     586                 :            : 
     587                 :       3620 :                         err = insert_data(trie, match_list, line, filename, file_priority, line_number, compat);
     588         [ +  + ]:       3620 :                         if (err < 0)
     589                 :          4 :                                 r = err;
     590                 :       3620 :                         break;
     591                 :    1065952 :                 };
     592                 :            :         }
     593                 :            : 
     594         [ +  + ]:         80 :         if (state == HW_MATCH)
     595         [ +  - ]:          4 :                 log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
     596                 :            :                            "Property expected, ignoring record with no properties");
     597                 :            : 
     598                 :         80 :         return r;
     599                 :            : }
     600                 :            : 
     601                 :          8 : int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat) {
     602                 :          8 :         _cleanup_free_ char *hwdb_bin = NULL;
     603                 :          8 :         _cleanup_(trie_freep) struct trie *trie = NULL;
     604                 :          8 :         _cleanup_strv_free_ char **files = NULL;
     605                 :            :         char **f;
     606                 :          8 :         uint16_t file_priority = 1;
     607                 :          8 :         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                 :          8 :         trie = new0(struct trie, 1);
     615         [ -  + ]:          8 :         if (!trie)
     616                 :          0 :                 return -ENOMEM;
     617                 :            : 
     618                 :            :         /* string store */
     619                 :          8 :         trie->strings = strbuf_new();
     620         [ -  + ]:          8 :         if (!trie->strings)
     621                 :          0 :                 return -ENOMEM;
     622                 :            : 
     623                 :            :         /* index */
     624                 :          8 :         trie->root = new0(struct trie_node, 1);
     625         [ -  + ]:          8 :         if (!trie->root)
     626                 :          0 :                 return -ENOMEM;
     627                 :            : 
     628                 :          8 :         trie->nodes_count++;
     629                 :            : 
     630                 :          8 :         err = conf_files_list_strv(&files, ".hwdb", root, 0, conf_file_dirs);
     631         [ -  + ]:          8 :         if (err < 0)
     632         [ #  # ]:          0 :                 return log_error_errno(err, "Failed to enumerate hwdb files: %m");
     633                 :            : 
     634   [ +  -  +  + ]:         88 :         STRV_FOREACH(f, files) {
     635         [ -  + ]:         80 :                 log_debug("Reading file \"%s\"", *f);
     636                 :         80 :                 err = import_file(trie, *f, file_priority++, compat);
     637   [ -  +  #  # ]:         80 :                 if (err < 0 && strict)
     638                 :          0 :                         r = err;
     639                 :            :         }
     640                 :            : 
     641                 :          8 :         strbuf_complete(trie->strings);
     642                 :            : 
     643         [ -  + ]:          8 :         log_debug("=== trie in-memory ===");
     644         [ -  + ]:          8 :         log_debug("nodes:            %8zu bytes (%8zu)",
     645                 :            :                   trie->nodes_count * sizeof(struct trie_node), trie->nodes_count);
     646         [ -  + ]:          8 :         log_debug("children arrays:  %8zu bytes (%8zu)",
     647                 :            :                   trie->children_count * sizeof(struct trie_child_entry), trie->children_count);
     648         [ -  + ]:          8 :         log_debug("values arrays:    %8zu bytes (%8zu)",
     649                 :            :                   trie->values_count * sizeof(struct trie_value_entry), trie->values_count);
     650         [ -  + ]:          8 :         log_debug("strings:          %8zu bytes",
     651                 :            :                   trie->strings->len);
     652         [ -  + ]:          8 :         log_debug("strings incoming: %8zu bytes (%8zu)",
     653                 :            :                   trie->strings->in_len, trie->strings->in_count);
     654         [ -  + ]:          8 :         log_debug("strings dedup'ed: %8zu bytes (%8zu)",
     655                 :            :                   trie->strings->dedup_len, trie->strings->dedup_count);
     656                 :            : 
     657         [ -  + ]:          8 :         hwdb_bin = path_join(root, hwdb_bin_dir ?: default_hwdb_bin_dir, "hwdb.bin");
     658         [ -  + ]:          8 :         if (!hwdb_bin)
     659                 :          0 :                 return -ENOMEM;
     660                 :            : 
     661                 :          8 :         mkdir_parents_label(hwdb_bin, 0755);
     662                 :          8 :         err = trie_store(trie, hwdb_bin, compat);
     663         [ -  + ]:          8 :         if (err < 0)
     664         [ #  # ]:          0 :                 return log_error_errno(err, "Failed to write database %s: %m", hwdb_bin);
     665                 :            : 
     666                 :          8 :         err = label_fix(hwdb_bin, 0);
     667         [ -  + ]:          8 :         if (err < 0)
     668                 :          0 :                 return err;
     669                 :            : 
     670                 :          8 :         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