static int hwdb_add_property(sd_hwdb *hwdb, const struct trie_value_entry_f *entry) { const char *key; int r; assert(hwdb); key = trie_string(hwdb, entry->key_off); /* * Silently ignore all properties which do not start with a * space; future extensions might use additional prefixes. */ if (key[0] != ' ') return 0; key++; if (le64toh(hwdb->head->value_entry_size) >= sizeof(struct trie_value_entry2_f)) { const struct trie_value_entry2_f *old, *entry2; entry2 = (const struct trie_value_entry2_f *)entry; old = ordered_hashmap_get(hwdb->properties, key); if (old) { /* 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; } } r = ordered_hashmap_ensure_allocated(&hwdb->properties, &string_hash_ops); if (r < 0) return r; r = ordered_hashmap_replace(hwdb->properties, key, (void *)entry); if (r < 0) return r; hwdb->properties_modified = true; return 0; }
static int enumerate_dir_d( OrderedHashmap *top, OrderedHashmap *bottom, OrderedHashmap *drops, const char *toppath, const char *drop) { _cleanup_free_ char *unit = NULL; _cleanup_free_ char *path = NULL; _cleanup_strv_free_ char **list = NULL; char **file; char *c; int r; assert(!endswith(drop, "/")); path = strjoin(toppath, "/", drop); if (!path) return -ENOMEM; log_debug("Looking at %s", path); unit = strdup(drop); if (!unit) return -ENOMEM; c = strrchr(unit, '.'); if (!c) return -EINVAL; *c = 0; r = get_files_in_directory(path, &list); if (r < 0) return log_error_errno(r, "Failed to enumerate %s: %m", path); strv_sort(list); STRV_FOREACH(file, list) { OrderedHashmap *h; int k; char *p; char *d; if (!endswith(*file, ".conf")) continue; p = strjoin(path, "/", *file); if (!p) return -ENOMEM; d = p + strlen(toppath) + 1; log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p); k = ordered_hashmap_put(top, d, p); if (k >= 0) { p = strdup(p); if (!p) return -ENOMEM; d = p + strlen(toppath) + 1; } else if (k != -EEXIST) { free(p); return k; } log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p); free(ordered_hashmap_remove(bottom, d)); k = ordered_hashmap_put(bottom, d, p); if (k < 0) { free(p); return k; } h = ordered_hashmap_get(drops, unit); if (!h) { h = ordered_hashmap_new(&string_hash_ops); if (!h) return -ENOMEM; ordered_hashmap_put(drops, unit, h); unit = strdup(unit); if (!unit) return -ENOMEM; } p = strdup(p); if (!p) return -ENOMEM; log_debug("Adding to drops: %s %s %s %s %s", unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p); k = ordered_hashmap_put(h, basename(p), p); if (k < 0) { free(p); if (k != -EEXIST) return k; } }