LCOV - code coverage report
Current view: top level - libsystemd/sd-hwdb - sd-hwdb.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 0 247 0.0 %
Date: 2015-07-29 18:47:03 Functions: 0 23 0.0 %

          Line data    Source code
       1             : /***
       2             :   This file is part of systemd.
       3             : 
       4             :   Copyright 2012 Kay Sievers <kay@vrfy.org>
       5             :   Copyright 2008 Alan Jenkins <alan.christopher.jenkins@googlemail.com>
       6             :   Copyright 2014 Tom Gundersen <teg@jklm.no>
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <stdio.h>
      23             : #include <errno.h>
      24             : #include <string.h>
      25             : #include <inttypes.h>
      26             : #include <stdlib.h>
      27             : #include <fnmatch.h>
      28             : #include <sys/mman.h>
      29             : 
      30             : #include "sd-hwdb.h"
      31             : 
      32             : #include "hashmap.h"
      33             : #include "refcnt.h"
      34             : 
      35             : #include "hwdb-util.h"
      36             : #include "hwdb-internal.h"
      37             : 
      38             : struct sd_hwdb {
      39             :         RefCount n_ref;
      40             :         int refcount;
      41             : 
      42             :         FILE *f;
      43             :         struct stat st;
      44             :         union {
      45             :                 struct trie_header_f *head;
      46             :                 const char *map;
      47             :         };
      48             : 
      49             :         char *modalias;
      50             : 
      51             :         OrderedHashmap *properties;
      52             :         Iterator properties_iterator;
      53             :         bool properties_modified;
      54             : };
      55             : 
      56             : struct linebuf {
      57             :         char bytes[LINE_MAX];
      58             :         size_t size;
      59             :         size_t len;
      60             : };
      61             : 
      62           0 : static void linebuf_init(struct linebuf *buf) {
      63           0 :         buf->size = 0;
      64           0 :         buf->len = 0;
      65           0 : }
      66             : 
      67           0 : static const char *linebuf_get(struct linebuf *buf) {
      68           0 :         if (buf->len + 1 >= sizeof(buf->bytes))
      69           0 :                 return NULL;
      70           0 :         buf->bytes[buf->len] = '\0';
      71           0 :         return buf->bytes;
      72             : }
      73             : 
      74           0 : static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) {
      75           0 :         if (buf->len + len >= sizeof(buf->bytes))
      76           0 :                 return false;
      77           0 :         memcpy(buf->bytes + buf->len, s, len);
      78           0 :         buf->len += len;
      79           0 :         return true;
      80             : }
      81             : 
      82           0 : static bool linebuf_add_char(struct linebuf *buf, char c)
      83             : {
      84           0 :         if (buf->len + 1 >= sizeof(buf->bytes))
      85           0 :                 return false;
      86           0 :         buf->bytes[buf->len++] = c;
      87           0 :         return true;
      88             : }
      89             : 
      90           0 : static void linebuf_rem(struct linebuf *buf, size_t count) {
      91           0 :         assert(buf->len >= count);
      92           0 :         buf->len -= count;
      93           0 : }
      94             : 
      95           0 : static void linebuf_rem_char(struct linebuf *buf) {
      96           0 :         linebuf_rem(buf, 1);
      97           0 : }
      98             : 
      99           0 : static const struct trie_child_entry_f *trie_node_children(sd_hwdb *hwdb, const struct trie_node_f *node) {
     100           0 :         return (const struct trie_child_entry_f *)((const char *)node + le64toh(hwdb->head->node_size));
     101             : }
     102             : 
     103           0 : static const struct trie_value_entry_f *trie_node_values(sd_hwdb *hwdb, const struct trie_node_f *node) {
     104           0 :         const char *base = (const char *)node;
     105             : 
     106           0 :         base += le64toh(hwdb->head->node_size);
     107           0 :         base += node->children_count * le64toh(hwdb->head->child_entry_size);
     108           0 :         return (const struct trie_value_entry_f *)base;
     109             : }
     110             : 
     111           0 : static const struct trie_node_f *trie_node_from_off(sd_hwdb *hwdb, le64_t off) {
     112           0 :         return (const struct trie_node_f *)(hwdb->map + le64toh(off));
     113             : }
     114             : 
     115           0 : static const char *trie_string(sd_hwdb *hwdb, le64_t off) {
     116           0 :         return hwdb->map + le64toh(off);
     117             : }
     118             : 
     119           0 : static int trie_children_cmp_f(const void *v1, const void *v2) {
     120           0 :         const struct trie_child_entry_f *n1 = v1;
     121           0 :         const struct trie_child_entry_f *n2 = v2;
     122             : 
     123           0 :         return n1->c - n2->c;
     124             : }
     125             : 
     126           0 : static const struct trie_node_f *node_lookup_f(sd_hwdb *hwdb, const struct trie_node_f *node, uint8_t c) {
     127             :         struct trie_child_entry_f *child;
     128             :         struct trie_child_entry_f search;
     129             : 
     130           0 :         search.c = c;
     131           0 :         child = bsearch(&search, trie_node_children(hwdb, node), node->children_count,
     132           0 :                         le64toh(hwdb->head->child_entry_size), trie_children_cmp_f);
     133           0 :         if (child)
     134           0 :                 return trie_node_from_off(hwdb, child->child_off);
     135           0 :         return NULL;
     136             : }
     137             : 
     138           0 : static int hwdb_add_property(sd_hwdb *hwdb, const char *key, const char *value) {
     139             :         int r;
     140             : 
     141           0 :         assert(hwdb);
     142           0 :         assert(key);
     143           0 :         assert(value);
     144             : 
     145             :         /*
     146             :          * Silently ignore all properties which do not start with a
     147             :          * space; future extensions might use additional prefixes.
     148             :          */
     149           0 :         if (key[0] != ' ')
     150           0 :                 return 0;
     151             : 
     152           0 :         key++;
     153             : 
     154           0 :         r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops);
     155           0 :         if (r < 0)
     156           0 :                 return r;
     157             : 
     158           0 :         r = ordered_hashmap_replace(hwdb->properties, key, (char*)value);
     159           0 :         if (r < 0)
     160           0 :                 return r;
     161             : 
     162           0 :         hwdb->properties_modified = true;
     163             : 
     164           0 :         return 0;
     165             : }
     166             : 
     167           0 : static int trie_fnmatch_f(sd_hwdb *hwdb, const struct trie_node_f *node, size_t p,
     168             :                           struct linebuf *buf, const char *search) {
     169             :         size_t len;
     170             :         size_t i;
     171             :         const char *prefix;
     172             :         int err;
     173             : 
     174           0 :         prefix = trie_string(hwdb, node->prefix_off);
     175           0 :         len = strlen(prefix + p);
     176           0 :         linebuf_add(buf, prefix + p, len);
     177             : 
     178           0 :         for (i = 0; i < node->children_count; i++) {
     179           0 :                 const struct trie_child_entry_f *child = &trie_node_children(hwdb, node)[i];
     180             : 
     181           0 :                 linebuf_add_char(buf, child->c);
     182           0 :                 err = trie_fnmatch_f(hwdb, trie_node_from_off(hwdb, child->child_off), 0, buf, search);
     183           0 :                 if (err < 0)
     184           0 :                         return err;
     185           0 :                 linebuf_rem_char(buf);
     186             :         }
     187             : 
     188           0 :         if (le64toh(node->values_count) && fnmatch(linebuf_get(buf), search, 0) == 0)
     189           0 :                 for (i = 0; i < le64toh(node->values_count); i++) {
     190           0 :                         err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[i].key_off),
     191           0 :                                                 trie_string(hwdb, trie_node_values(hwdb, node)[i].value_off));
     192           0 :                         if (err < 0)
     193           0 :                                 return err;
     194             :                 }
     195             : 
     196           0 :         linebuf_rem(buf, len);
     197           0 :         return 0;
     198             : }
     199             : 
     200           0 : static int trie_search_f(sd_hwdb *hwdb, const char *search) {
     201             :         struct linebuf buf;
     202             :         const struct trie_node_f *node;
     203           0 :         size_t i = 0;
     204             :         int err;
     205             : 
     206           0 :         linebuf_init(&buf);
     207             : 
     208           0 :         node = trie_node_from_off(hwdb, hwdb->head->nodes_root_off);
     209           0 :         while (node) {
     210             :                 const struct trie_node_f *child;
     211           0 :                 size_t p = 0;
     212             : 
     213           0 :                 if (node->prefix_off) {
     214             :                         uint8_t c;
     215             : 
     216           0 :                         for (; (c = trie_string(hwdb, node->prefix_off)[p]); p++) {
     217           0 :                                 if (c == '*' || c == '?' || c == '[')
     218           0 :                                         return trie_fnmatch_f(hwdb, node, p, &buf, search + i + p);
     219           0 :                                 if (c != search[i + p])
     220           0 :                                         return 0;
     221             :                         }
     222           0 :                         i += p;
     223             :                 }
     224             : 
     225           0 :                 child = node_lookup_f(hwdb, node, '*');
     226           0 :                 if (child) {
     227           0 :                         linebuf_add_char(&buf, '*');
     228           0 :                         err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
     229           0 :                         if (err < 0)
     230           0 :                                 return err;
     231           0 :                         linebuf_rem_char(&buf);
     232             :                 }
     233             : 
     234           0 :                 child = node_lookup_f(hwdb, node, '?');
     235           0 :                 if (child) {
     236           0 :                         linebuf_add_char(&buf, '?');
     237           0 :                         err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
     238           0 :                         if (err < 0)
     239           0 :                                 return err;
     240           0 :                         linebuf_rem_char(&buf);
     241             :                 }
     242             : 
     243           0 :                 child = node_lookup_f(hwdb, node, '[');
     244           0 :                 if (child) {
     245           0 :                         linebuf_add_char(&buf, '[');
     246           0 :                         err = trie_fnmatch_f(hwdb, child, 0, &buf, search + i);
     247           0 :                         if (err < 0)
     248           0 :                                 return err;
     249           0 :                         linebuf_rem_char(&buf);
     250             :                 }
     251             : 
     252           0 :                 if (search[i] == '\0') {
     253             :                         size_t n;
     254             : 
     255           0 :                         for (n = 0; n < le64toh(node->values_count); n++) {
     256           0 :                                 err = hwdb_add_property(hwdb, trie_string(hwdb, trie_node_values(hwdb, node)[n].key_off),
     257           0 :                                                         trie_string(hwdb, trie_node_values(hwdb, node)[n].value_off));
     258           0 :                                 if (err < 0)
     259           0 :                                         return err;
     260             :                         }
     261           0 :                         return 0;
     262             :                 }
     263             : 
     264           0 :                 child = node_lookup_f(hwdb, node, search[i]);
     265           0 :                 node = child;
     266           0 :                 i++;
     267             :         }
     268           0 :         return 0;
     269             : }
     270             : 
     271             : static const char hwdb_bin_paths[] =
     272             :     "/etc/systemd/hwdb/hwdb.bin\0"
     273             :     "/etc/udev/hwdb.bin\0"
     274             :     "/usr/lib/systemd/hwdb/hwdb.bin\0"
     275             : #ifdef HAVE_SPLIT_USR
     276             :     "/lib/systemd/hwdb/hwdb.bin\0"
     277             : #endif
     278             :     UDEVLIBEXECDIR "/hwdb.bin\0";
     279             : 
     280           0 : _public_ int sd_hwdb_new(sd_hwdb **ret) {
     281           0 :         _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL;
     282             :         const char *hwdb_bin_path;
     283           0 :         const char sig[] = HWDB_SIG;
     284             : 
     285           0 :         assert_return(ret, -EINVAL);
     286             : 
     287           0 :         hwdb = new0(sd_hwdb, 1);
     288           0 :         if (!hwdb)
     289           0 :                 return -ENOMEM;
     290             : 
     291           0 :         hwdb->n_ref = REFCNT_INIT;
     292             : 
     293             :         /* find hwdb.bin in hwdb_bin_paths */
     294           0 :         NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) {
     295           0 :                 hwdb->f = fopen(hwdb_bin_path, "re");
     296           0 :                 if (hwdb->f)
     297           0 :                         break;
     298           0 :                 else if (errno == ENOENT)
     299           0 :                         continue;
     300             :                 else
     301           0 :                         return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
     302             :         }
     303             : 
     304           0 :         if (!hwdb->f) {
     305           0 :                 log_debug("hwdb.bin does not exist, please run udevadm hwdb --update");
     306           0 :                 return -ENOENT;
     307             :         }
     308             : 
     309           0 :         if (fstat(fileno(hwdb->f), &hwdb->st) < 0 ||
     310           0 :             (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8)
     311           0 :                 return log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path);
     312             : 
     313           0 :         hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
     314           0 :         if (hwdb->map == MAP_FAILED)
     315           0 :                 return log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path);
     316             : 
     317           0 :         if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
     318           0 :             (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) {
     319           0 :                 log_debug("error recognizing the format of %s", hwdb_bin_path);
     320           0 :                 return -EINVAL;
     321             :         }
     322             : 
     323           0 :         log_debug("=== trie on-disk ===");
     324           0 :         log_debug("tool version:          %"PRIu64, le64toh(hwdb->head->tool_version));
     325           0 :         log_debug("file size:        %8"PRIi64" bytes", hwdb->st.st_size);
     326           0 :         log_debug("header size       %8"PRIu64" bytes", le64toh(hwdb->head->header_size));
     327           0 :         log_debug("strings           %8"PRIu64" bytes", le64toh(hwdb->head->strings_len));
     328           0 :         log_debug("nodes             %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len));
     329             : 
     330           0 :         *ret = hwdb;
     331           0 :         hwdb = NULL;
     332             : 
     333           0 :         return 0;
     334             : }
     335             : 
     336           0 : _public_ sd_hwdb *sd_hwdb_ref(sd_hwdb *hwdb) {
     337           0 :         assert_return(hwdb, NULL);
     338             : 
     339           0 :         assert_se(REFCNT_INC(hwdb->n_ref) >= 2);
     340             : 
     341           0 :         return hwdb;
     342             : }
     343             : 
     344           0 : _public_ sd_hwdb *sd_hwdb_unref(sd_hwdb *hwdb) {
     345           0 :         if (hwdb && REFCNT_DEC(hwdb->n_ref) == 0) {
     346           0 :                 if (hwdb->map)
     347           0 :                         munmap((void *)hwdb->map, hwdb->st.st_size);
     348           0 :                 if (hwdb->f)
     349           0 :                         fclose(hwdb->f);
     350           0 :                 free(hwdb->modalias);
     351           0 :                 ordered_hashmap_free(hwdb->properties);
     352           0 :                 free(hwdb);
     353             :         }
     354             : 
     355           0 :         return NULL;
     356             : }
     357             : 
     358           0 : bool hwdb_validate(sd_hwdb *hwdb) {
     359           0 :         bool found = false;
     360             :         const char* p;
     361             :         struct stat st;
     362             : 
     363           0 :         if (!hwdb)
     364           0 :                 return false;
     365           0 :         if (!hwdb->f)
     366           0 :                 return false;
     367             : 
     368             :         /* if hwdb.bin doesn't exist anywhere, we need to update */
     369           0 :         NULSTR_FOREACH(p, hwdb_bin_paths) {
     370           0 :                 if (stat(p, &st) >= 0) {
     371           0 :                         found = true;
     372           0 :                         break;
     373             :                 }
     374             :         }
     375           0 :         if (!found)
     376           0 :                 return true;
     377             : 
     378           0 :         if (timespec_load(&hwdb->st.st_mtim) != timespec_load(&st.st_mtim))
     379           0 :                 return true;
     380           0 :         return false;
     381             : }
     382             : 
     383           0 : static int properties_prepare(sd_hwdb *hwdb, const char *modalias) {
     384           0 :         _cleanup_free_ char *mod = NULL;
     385             :         int r;
     386             : 
     387           0 :         assert(hwdb);
     388           0 :         assert(modalias);
     389             : 
     390           0 :         if (streq_ptr(modalias, hwdb->modalias))
     391           0 :                 return 0;
     392             : 
     393           0 :         mod = strdup(modalias);
     394           0 :         if (!mod)
     395           0 :                 return -ENOMEM;
     396             : 
     397           0 :         ordered_hashmap_clear(hwdb->properties);
     398             : 
     399           0 :         hwdb->properties_modified = true;
     400             : 
     401           0 :         r = trie_search_f(hwdb, modalias);
     402           0 :         if (r < 0)
     403           0 :                 return r;
     404             : 
     405           0 :         free(hwdb->modalias);
     406           0 :         hwdb->modalias = mod;
     407           0 :         mod = NULL;
     408             : 
     409           0 :         return 0;
     410             : }
     411             : 
     412           0 : _public_ int sd_hwdb_get(sd_hwdb *hwdb, const char *modalias, const char *key, const char **_value) {
     413             :         const char *value;
     414             :         int r;
     415             : 
     416           0 :         assert_return(hwdb, -EINVAL);
     417           0 :         assert_return(hwdb->f, -EINVAL);
     418           0 :         assert_return(modalias, -EINVAL);
     419           0 :         assert_return(_value, -EINVAL);
     420             : 
     421           0 :         r = properties_prepare(hwdb, modalias);
     422           0 :         if (r < 0)
     423           0 :                 return r;
     424             : 
     425           0 :         value = ordered_hashmap_get(hwdb->properties, key);
     426           0 :         if (!value)
     427           0 :                 return -ENOENT;
     428             : 
     429           0 :         *_value = value;
     430             : 
     431           0 :         return 0;
     432             : }
     433             : 
     434           0 : _public_ int sd_hwdb_seek(sd_hwdb *hwdb, const char *modalias) {
     435             :         int r;
     436             : 
     437           0 :         assert_return(hwdb, -EINVAL);
     438           0 :         assert_return(hwdb->f, -EINVAL);
     439           0 :         assert_return(modalias, -EINVAL);
     440             : 
     441           0 :         r = properties_prepare(hwdb, modalias);
     442           0 :         if (r < 0)
     443           0 :                 return r;
     444             : 
     445           0 :         hwdb->properties_modified = false;
     446           0 :         hwdb->properties_iterator = ITERATOR_FIRST;
     447             : 
     448           0 :         return 0;
     449             : }
     450             : 
     451           0 : _public_ int sd_hwdb_enumerate(sd_hwdb *hwdb, const char **key, const char **value) {
     452             :         const void *k;
     453             :         void *v;
     454             : 
     455           0 :         assert_return(hwdb, -EINVAL);
     456           0 :         assert_return(key, -EINVAL);
     457           0 :         assert_return(value, -EINVAL);
     458             : 
     459           0 :         if (hwdb->properties_modified)
     460           0 :                 return -EAGAIN;
     461             : 
     462           0 :         ordered_hashmap_iterate(hwdb->properties, &hwdb->properties_iterator, &v, &k);
     463           0 :         if (!k)
     464           0 :                 return 0;
     465             : 
     466           0 :         *key = k;
     467           0 :         *value = v;
     468             : 
     469           0 :         return 1;
     470             : }

Generated by: LCOV version 1.11