diff options
Diffstat (limited to 'src/libsystemd/sd-hwdb/sd-hwdb.c')
-rw-r--r-- | src/libsystemd/sd-hwdb/sd-hwdb.c | 36 |
1 files changed, 32 insertions, 4 deletions
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; } } |