2 This file is part of systemd.
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>
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.
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.
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/>.
32 #include "alloc-util.h"
35 #include "hwdb-internal.h"
36 #include "hwdb-util.h"
38 #include "string-util.h"
47 struct trie_header_f
*head
;
51 OrderedHashmap
*properties
;
52 Iterator properties_iterator
;
53 bool properties_modified
;
62 static void linebuf_init(struct linebuf
*buf
) {
67 static const char *linebuf_get(struct linebuf
*buf
) {
68 if (buf
->len
+ 1 >= sizeof(buf
->bytes
))
70 buf
->bytes
[buf
->len
] = '\0';
74 static bool linebuf_add(struct linebuf
*buf
, const char *s
, size_t len
) {
75 if (buf
->len
+ len
>= sizeof(buf
->bytes
))
77 memcpy(buf
->bytes
+ buf
->len
, s
, len
);
82 static bool linebuf_add_char(struct linebuf
*buf
, char c
) {
83 if (buf
->len
+ 1 >= sizeof(buf
->bytes
))
85 buf
->bytes
[buf
->len
++] = c
;
89 static void linebuf_rem(struct linebuf
*buf
, size_t count
) {
90 assert(buf
->len
>= count
);
94 static void linebuf_rem_char(struct linebuf
*buf
) {
98 static const struct trie_child_entry_f
*trie_node_child(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t idx
) {
99 const char *base
= (const char *)node
;
101 base
+= le64toh(hwdb
->head
->node_size
);
102 base
+= idx
* le64toh(hwdb
->head
->child_entry_size
);
103 return (const struct trie_child_entry_f
*)base
;
106 static const struct trie_value_entry_f
*trie_node_value(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t idx
) {
107 const char *base
= (const char *)node
;
109 base
+= le64toh(hwdb
->head
->node_size
);
110 base
+= node
->children_count
* le64toh(hwdb
->head
->child_entry_size
);
111 base
+= idx
* le64toh(hwdb
->head
->value_entry_size
);
112 return (const struct trie_value_entry_f
*)base
;
115 static const struct trie_node_f
*trie_node_from_off(sd_hwdb
*hwdb
, le64_t off
) {
116 return (const struct trie_node_f
*)(hwdb
->map
+ le64toh(off
));
119 static const char *trie_string(sd_hwdb
*hwdb
, le64_t off
) {
120 return hwdb
->map
+ le64toh(off
);
123 static int trie_children_cmp_f(const void *v1
, const void *v2
) {
124 const struct trie_child_entry_f
*n1
= v1
;
125 const struct trie_child_entry_f
*n2
= v2
;
127 return n1
->c
- n2
->c
;
130 static const struct trie_node_f
*node_lookup_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, uint8_t c
) {
131 struct trie_child_entry_f
*child
;
132 struct trie_child_entry_f search
;
135 child
= bsearch(&search
, (const char *)node
+ le64toh(hwdb
->head
->node_size
), node
->children_count
,
136 le64toh(hwdb
->head
->child_entry_size
), trie_children_cmp_f
);
138 return trie_node_from_off(hwdb
, child
->child_off
);
142 static int hwdb_add_property(sd_hwdb
*hwdb
, const struct trie_value_entry_f
*entry
) {
148 key
= trie_string(hwdb
, entry
->key_off
);
151 * Silently ignore all properties which do not start with a
152 * space; future extensions might use additional prefixes.
159 if (le64toh(hwdb
->head
->value_entry_size
) >= sizeof(struct trie_value_entry2_f
)) {
160 const struct trie_value_entry2_f
*old
, *entry2
;
162 entry2
= (const struct trie_value_entry2_f
*)entry
;
163 old
= ordered_hashmap_get(hwdb
->properties
, key
);
165 /* On duplicates, we order by filename priority and line-number.
168 * v2 of the format had 64 bits for the line number.
169 * v3 reuses top 32 bits of line_number to store the priority.
170 * We check the top bits — if they are zero we have v2 format.
171 * This means that v2 clients will print wrong line numbers with
174 * For v3 data: we compare the priority (of the source file)
175 * and the line number.
177 * For v2 data: we rely on the fact that the filenames in the hwdb
178 * are added in the order of priority (higher later), because they
179 * are *processed* in the order of priority. So we compare the
180 * indices to determine which file had higher priority. Comparing
181 * the strings alphabetically would be useless, because those are
182 * full paths, and e.g. /usr/lib would sort after /etc, even
183 * though it has lower priority. This is not reliable because of
184 * suffix compression, but should work for the most common case of
185 * /usr/lib/udev/hwbd.d and /etc/udev/hwdb.d, and is better than
186 * not doing the comparison at all.
190 if (entry2
->file_priority
== 0)
191 lower
= entry2
->filename_off
< old
->filename_off
||
192 (entry2
->filename_off
== old
->filename_off
&& entry2
->line_number
< old
->line_number
);
194 lower
= entry2
->file_priority
< old
->file_priority
||
195 (entry2
->file_priority
== old
->file_priority
&& entry2
->line_number
< old
->line_number
);
201 r
= ordered_hashmap_ensure_allocated(&hwdb
->properties
, &string_hash_ops
);
205 r
= ordered_hashmap_replace(hwdb
->properties
, key
, (void *)entry
);
209 hwdb
->properties_modified
= true;
214 static int trie_fnmatch_f(sd_hwdb
*hwdb
, const struct trie_node_f
*node
, size_t p
,
215 struct linebuf
*buf
, const char *search
) {
221 prefix
= trie_string(hwdb
, node
->prefix_off
);
222 len
= strlen(prefix
+ p
);
223 linebuf_add(buf
, prefix
+ p
, len
);
225 for (i
= 0; i
< node
->children_count
; i
++) {
226 const struct trie_child_entry_f
*child
= trie_node_child(hwdb
, node
, i
);
228 linebuf_add_char(buf
, child
->c
);
229 err
= trie_fnmatch_f(hwdb
, trie_node_from_off(hwdb
, child
->child_off
), 0, buf
, search
);
232 linebuf_rem_char(buf
);
235 if (le64toh(node
->values_count
) && fnmatch(linebuf_get(buf
), search
, 0) == 0)
236 for (i
= 0; i
< le64toh(node
->values_count
); i
++) {
237 err
= hwdb_add_property(hwdb
, trie_node_value(hwdb
, node
, i
));
242 linebuf_rem(buf
, len
);
246 static int trie_search_f(sd_hwdb
*hwdb
, const char *search
) {
248 const struct trie_node_f
*node
;
254 node
= trie_node_from_off(hwdb
, hwdb
->head
->nodes_root_off
);
256 const struct trie_node_f
*child
;
259 if (node
->prefix_off
) {
262 for (; (c
= trie_string(hwdb
, node
->prefix_off
)[p
]); p
++) {
263 if (IN_SET(c
, '*', '?', '['))
264 return trie_fnmatch_f(hwdb
, node
, p
, &buf
, search
+ i
+ p
);
265 if (c
!= search
[i
+ p
])
271 child
= node_lookup_f(hwdb
, node
, '*');
273 linebuf_add_char(&buf
, '*');
274 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
277 linebuf_rem_char(&buf
);
280 child
= node_lookup_f(hwdb
, node
, '?');
282 linebuf_add_char(&buf
, '?');
283 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
286 linebuf_rem_char(&buf
);
289 child
= node_lookup_f(hwdb
, node
, '[');
291 linebuf_add_char(&buf
, '[');
292 err
= trie_fnmatch_f(hwdb
, child
, 0, &buf
, search
+ i
);
295 linebuf_rem_char(&buf
);
298 if (search
[i
] == '\0') {
301 for (n
= 0; n
< le64toh(node
->values_count
); n
++) {
302 err
= hwdb_add_property(hwdb
, trie_node_value(hwdb
, node
, n
));
309 child
= node_lookup_f(hwdb
, node
, search
[i
]);
316 static const char hwdb_bin_paths
[] =
317 "/etc/systemd/hwdb/hwdb.bin\0"
318 "/etc/udev/hwdb.bin\0"
319 "/usr/lib/systemd/hwdb/hwdb.bin\0"
321 "/lib/systemd/hwdb/hwdb.bin\0"
323 UDEVLIBEXECDIR
"/hwdb.bin\0";
325 _public_
int sd_hwdb_new(sd_hwdb
**ret
) {
326 _cleanup_(sd_hwdb_unrefp
) sd_hwdb
*hwdb
= NULL
;
327 const char *hwdb_bin_path
;
328 const char sig
[] = HWDB_SIG
;
330 assert_return(ret
, -EINVAL
);
332 hwdb
= new0(sd_hwdb
, 1);
336 hwdb
->n_ref
= REFCNT_INIT
;
338 /* find hwdb.bin in hwdb_bin_paths */
339 NULSTR_FOREACH(hwdb_bin_path
, hwdb_bin_paths
) {
340 hwdb
->f
= fopen(hwdb_bin_path
, "re");
343 else if (errno
== ENOENT
)
346 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
350 log_debug("hwdb.bin does not exist, please run systemd-hwdb update");
354 if (fstat(fileno(hwdb
->f
), &hwdb
->st
) < 0 ||
355 (size_t)hwdb
->st
.st_size
< offsetof(struct trie_header_f
, strings_len
) + 8)
356 return log_debug_errno(errno
, "error reading %s: %m", hwdb_bin_path
);
358 hwdb
->map
= mmap(0, hwdb
->st
.st_size
, PROT_READ
, MAP_SHARED
, fileno(hwdb
->f
), 0);
359 if (hwdb
->map
== MAP_FAILED
)
360 return log_debug_errno(errno
, "error mapping %s: %m", hwdb_bin_path
);
362 if (memcmp(hwdb
->map
, sig
, sizeof(hwdb
->head
->signature
)) != 0 ||
363 (size_t)hwdb
->st
.st_size
!= le64toh(hwdb
->head
->file_size
)) {
364 log_debug("error recognizing the format of %s", hwdb_bin_path
);
368 log_debug("=== trie on-disk ===");
369 log_debug("tool version: %"PRIu64
, le64toh(hwdb
->head
->tool_version
));
370 log_debug("file size: %8"PRIi64
" bytes", hwdb
->st
.st_size
);
371 log_debug("header size %8"PRIu64
" bytes", le64toh(hwdb
->head
->header_size
));
372 log_debug("strings %8"PRIu64
" bytes", le64toh(hwdb
->head
->strings_len
));
373 log_debug("nodes %8"PRIu64
" bytes", le64toh(hwdb
->head
->nodes_len
));
381 _public_ sd_hwdb
*sd_hwdb_ref(sd_hwdb
*hwdb
) {
382 assert_return(hwdb
, NULL
);
384 assert_se(REFCNT_INC(hwdb
->n_ref
) >= 2);
389 _public_ sd_hwdb
*sd_hwdb_unref(sd_hwdb
*hwdb
) {
390 if (hwdb
&& REFCNT_DEC(hwdb
->n_ref
) == 0) {
392 munmap((void *)hwdb
->map
, hwdb
->st
.st_size
);
393 safe_fclose(hwdb
->f
);
394 ordered_hashmap_free(hwdb
->properties
);
401 bool hwdb_validate(sd_hwdb
*hwdb
) {
411 /* if hwdb.bin doesn't exist anywhere, we need to update */
412 NULSTR_FOREACH(p
, hwdb_bin_paths
) {
413 if (stat(p
, &st
) >= 0) {
421 if (timespec_load(&hwdb
->st
.st_mtim
) != timespec_load(&st
.st_mtim
))
426 static int properties_prepare(sd_hwdb
*hwdb
, const char *modalias
) {
430 ordered_hashmap_clear(hwdb
->properties
);
431 hwdb
->properties_modified
= true;
433 return trie_search_f(hwdb
, modalias
);
436 _public_
int sd_hwdb_get(sd_hwdb
*hwdb
, const char *modalias
, const char *key
, const char **_value
) {
437 const struct trie_value_entry_f
*entry
;
440 assert_return(hwdb
, -EINVAL
);
441 assert_return(hwdb
->f
, -EINVAL
);
442 assert_return(modalias
, -EINVAL
);
443 assert_return(_value
, -EINVAL
);
445 r
= properties_prepare(hwdb
, modalias
);
449 entry
= ordered_hashmap_get(hwdb
->properties
, key
);
453 *_value
= trie_string(hwdb
, entry
->value_off
);
458 _public_
int sd_hwdb_seek(sd_hwdb
*hwdb
, const char *modalias
) {
461 assert_return(hwdb
, -EINVAL
);
462 assert_return(hwdb
->f
, -EINVAL
);
463 assert_return(modalias
, -EINVAL
);
465 r
= properties_prepare(hwdb
, modalias
);
469 hwdb
->properties_modified
= false;
470 hwdb
->properties_iterator
= ITERATOR_FIRST
;
475 _public_
int sd_hwdb_enumerate(sd_hwdb
*hwdb
, const char **key
, const char **value
) {
476 const struct trie_value_entry_f
*entry
;
479 assert_return(hwdb
, -EINVAL
);
480 assert_return(key
, -EINVAL
);
481 assert_return(value
, -EINVAL
);
483 if (hwdb
->properties_modified
)
486 ordered_hashmap_iterate(hwdb
->properties
, &hwdb
->properties_iterator
, (void **)&entry
, &k
);
491 *value
= trie_string(hwdb
, entry
->value_off
);