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 : : }
|