diff options
Diffstat (limited to 'src/libsystemd')
-rw-r--r-- | src/libsystemd/sd-hwdb/hwdb-internal.h | 4 | ||||
-rw-r--r-- | src/libsystemd/sd-hwdb/sd-hwdb.c | 36 |
2 files changed, 35 insertions, 5 deletions
diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h index 4fff94ec76..2f4934ae8b 100644 --- a/src/libsystemd/sd-hwdb/hwdb-internal.h +++ b/src/libsystemd/sd-hwdb/hwdb-internal.h @@ -76,5 +76,7 @@ struct trie_value_entry2_f { le64_t key_off; le64_t value_off; le64_t filename_off; - le64_t line_number; + le32_t line_number; + le16_t file_priority; + le16_t padding; } _packed_; diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c index 811a60f0c3..a8c6376302 100644 --- a/src/libsystemd/sd-hwdb/sd-hwdb.c +++ b/src/libsystemd/sd-hwdb/sd-hwdb.c @@ -164,10 +164,38 @@ static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *ent entry2 = (const struct trie_value_entry2_f *)entry; old = ordered_hashmap_get(hwdb->properties, key); if (old) { - /* on duplicates, we order by filename and line-number */ - r = strcmp(trie_string(hwdb, entry2->filename_off), trie_string(hwdb, old->filename_off)); - if (r < 0 || - (r == 0 && entry2->line_number < old->line_number)) + /* On duplicates, we order by filename priority and line-number. + * + * + * v2 of the format had 64 bits for the line number. + * v3 reuses top 32 bits of line_number to store the priority. + * We check the top bits — if they are zero we have v2 format. + * This means that v2 clients will print wrong line numbers with + * v3 data. + * + * For v3 data: we compare the priority (of the source file) + * and the line number. + * + * For v2 data: we rely on the fact that the filenames in the hwdb + * are added in the order of priority (higher later), because they + * are *processed* in the order of priority. So we compare the + * indices to determine which file had higher priority. Comparing + * the strings alphabetically would be useless, because those are + * full paths, and e.g. /usr/lib would sort after /etc, even + * though it has lower priority. This is not reliable because of + * suffix compression, but should work for the most common case of + * /usr/lib/udev/hwbd.d and /etc/udev/hwdb.d, and is better than + * not doing the comparison at all. + */ + bool lower; + + if (entry2->file_priority == 0) + lower = entry2->filename_off < old->filename_off || + (entry2->filename_off == old->filename_off && entry2->line_number < old->line_number); + else + lower = entry2->file_priority < old->file_priority || + (entry2->file_priority == old->file_priority && entry2->line_number < old->line_number); + if (lower) return 0; } } |