/* calculate the storage space for the nodes, children arrays, value arrays */ static void trie_store_nodes_size(struct trie_f *trie, struct trie_node *node) { uint64_t i; for (i = 0; i < node->children_count; i++) trie_store_nodes_size(trie, node->children[i].child); trie->strings_off += sizeof(struct trie_node_f); for (i = 0; i < node->children_count; i++) trie->strings_off += sizeof(struct trie_child_entry_f); for (i = 0; i < node->values_count; i++) trie->strings_off += sizeof(struct trie_value_entry_f); }
static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { uint64_t i; struct trie_node_f n = { .prefix_off = htole64(trie->strings_off + node->prefix_off), .children_count = node->children_count, .values_count = htole64(node->values_count), }; struct trie_child_entry_f *children = NULL; int64_t node_off; if (node->children_count) { children = new0(struct trie_child_entry_f, node->children_count); if (!children) return -ENOMEM; } /* post-order recursion */ for (i = 0; i < node->children_count; i++) { int64_t child_off; child_off = trie_store_nodes(trie, node->children[i].child); if (child_off < 0) { free(children); return child_off; } children[i].c = node->children[i].c; children[i].child_off = htole64(child_off); } /* write node */ node_off = ftello(trie->f); if (fwrite(&n, sizeof(struct trie_node_f), 1, trie->f) != 1) log_error("Failed to write sizeof struct trie_node_f to n in %s\n", "[udevadm-hwdb.c:trie_store_nodes]"); trie->nodes_count++; /* append children array */ if (node->children_count) { if (fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f) != node->children_count) log_error("Failed to write children_count in %s\n", "[udevadm-hwdb.c:trie_store_nodes]"); trie->children_count += node->children_count; free(children); } /* append values array */ for (i = 0; i < node->values_count; i++) { struct trie_value_entry_f v = { .key_off = htole64(trie->strings_off + node->values[i].key_off), .value_off = htole64(trie->strings_off + node->values[i].value_off), }; if (fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f) != 1) log_error("Failed to write sizeof trie_value_entry_f to v in %s\n", "[udevadm-hwdb.c:trie_store_nodes]"); trie->values_count++; } return node_off; } static int trie_store(struct trie *trie, const char *filename) { struct trie_f t = { .trie = trie, }; char *filename_tmp; int64_t pos; int64_t root_off; int64_t size; struct trie_header_f h = { .signature = HWDB_SIG, .tool_version = htole64(atoi(VERSION)), .header_size = htole64(sizeof(struct trie_header_f)), .node_size = htole64(sizeof(struct trie_node_f)), .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), }; int err; /* calculate size of header, nodes, children entries, value entries */ t.strings_off = sizeof(struct trie_header_f); trie_store_nodes_size(&t, trie->root); err = fopen_temporary(filename , &t.f, &filename_tmp); if (err < 0) return err; fchmod(fileno(t.f), 0444); /* write nodes */ fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); root_off = trie_store_nodes(&t, trie->root); h.nodes_root_off = htole64(root_off); pos = ftello(t.f); h.nodes_len = htole64(pos - sizeof(struct trie_header_f)); /* write string buffer */ if (fwrite(trie->strings->buf, trie->strings->len, 1, t.f) != 1) log_error("Failed to write into trie->strings->buf in %s\n", "[udevadm-hwdb.c:trie_store]"); h.strings_len = htole64(trie->strings->len); /* write header */ size = ftello(t.f); h.file_size = htole64(size); fseeko(t.f, 0, SEEK_SET); if (fwrite(&h, sizeof(struct trie_header_f), 1, t.f) != 1) log_error("Failed to write into h in %s\n", "[udevadm-hwdb.c:trie_store]"); err = ferror(t.f); if (err) err = -errno; fclose(t.f); if (err < 0 || rename(filename_tmp, filename) < 0) { unlink(filename_tmp); goto out; } log_debug("=== trie on-disk ===\n"); log_debug("size: %8llu bytes\n", (unsigned long long)size); log_debug("header: %8zu bytes\n", sizeof(struct trie_header_f)); log_debug("nodes: %8llu bytes (%8llu)\n", (unsigned long long)t.nodes_count * sizeof(struct trie_node_f), (unsigned long long)t.nodes_count); log_debug("child pointers: %8llu bytes (%8llu)\n", (unsigned long long)t.children_count * sizeof(struct trie_child_entry_f), (unsigned long long)t.children_count); log_debug("value pointers: %8llu bytes (%8llu)\n", (unsigned long long)t.values_count * sizeof(struct trie_value_entry_f), (unsigned long long)t.values_count); log_debug("string store: %8llu bytes\n", (unsigned long long)trie->strings->len); log_debug("strings start: %8llu\n", (unsigned long long) t.strings_off); out: free(filename_tmp); return err; } static int insert_data(struct trie *trie, struct udev_list *match_list, char *line, const char *filename) { char *value; struct udev_list_entry *entry; value = strchr(line, '='); if (!value) { log_error("Error, key/value pair expected but got '%s' in '%s':\n", line, filename); return -EINVAL; } value[0] = '\0'; value++; if (line[0] == '\0' || value[0] == '\0') { log_error("Error, empty key or value '%s' in '%s':\n", line, filename); return -EINVAL; } udev_list_entry_foreach(entry, udev_list_get_entry(match_list)) trie_insert(trie, trie->root, udev_list_entry_get_name(entry), line, value); return 0; } static int import_file(struct udev *udev, struct trie *trie, const char *filename) { enum { HW_MATCH, HW_DATA, HW_NONE, } state = HW_NONE; FILE *f; char line[LINE_MAX]; struct udev_list match_list; udev_list_init(udev, &match_list, false); f = fopen(filename, "re"); if (f == NULL) return -errno; while (fgets(line, sizeof(line), f)) { size_t len; char *pos; /* comment line */ if (line[0] == '#') continue; /* strip trailing comment */ pos = strchr(line, '#'); if (pos) pos[0] = '\0'; /* strip trailing whitespace */ len = strlen(line); while (len > 0 && isspace(line[len-1])) len--; line[len] = '\0'; switch (state) { case HW_NONE: if (len == 0) break; if (line[0] == ' ') { log_error("Error, MATCH expected but got '%s' in '%s':\n", line, filename); break; } /* start of record, first match */ state = HW_MATCH; udev_list_entry_add(&match_list, line, NULL); break; case HW_MATCH: if (len == 0) { log_error("Error, DATA expected but got empty line in '%s':\n", filename); state = HW_NONE; udev_list_cleanup(&match_list); break; } /* another match */ if (line[0] != ' ') { udev_list_entry_add(&match_list, line, NULL); break; } /* first data */ state = HW_DATA; insert_data(trie, &match_list, line, filename); break; case HW_DATA: /* end of record */ if (len == 0) { state = HW_NONE; udev_list_cleanup(&match_list); break; } if (line[0] != ' ') { log_error("Error, DATA expected but got '%s' in '%s':\n", line, filename); state = HW_NONE; udev_list_cleanup(&match_list); break; } insert_data(trie, &match_list, line, filename); break; }; } fclose(f); udev_list_cleanup(&match_list); return 0; } static void help(void) { printf("Usage: udevadm hwdb OPTIONS\n" " --update update the hardware database\n" " --test=<modalias> query database and print result\n" " --root=<path> alternative root path in the filesystem\n" " --help\n\n"); }
static int64_t trie_store_nodes(struct trie_f *trie, struct trie_node *node) { uint64_t i; struct trie_node_f n = { .prefix_off = htole64(trie->strings_off + node->prefix_off), .children_count = node->children_count, .values_count = htole64(node->values_count), }; struct trie_child_entry_f *children = NULL; int64_t node_off; if (node->children_count) { children = new0(struct trie_child_entry_f, node->children_count); if (!children) return -ENOMEM; } /* post-order recursion */ for (i = 0; i < node->children_count; i++) { int64_t child_off; child_off = trie_store_nodes(trie, node->children[i].child); if (child_off < 0) return child_off; children[i].c = node->children[i].c; children[i].child_off = htole64(child_off); } /* write node */ node_off = ftello(trie->f); fwrite(&n, sizeof(struct trie_node_f), 1, trie->f); trie->nodes_count++; /* append children array */ if (node->children_count) { fwrite(children, sizeof(struct trie_child_entry_f), node->children_count, trie->f); trie->children_count += node->children_count; free(children); } /* append values array */ for (i = 0; i < node->values_count; i++) { struct trie_value_entry_f v = { .key_off = htole64(trie->strings_off + node->values[i].key_off), .value_off = htole64(trie->strings_off + node->values[i].value_off), }; fwrite(&v, sizeof(struct trie_value_entry_f), 1, trie->f); trie->values_count++; } return node_off; } static int trie_store(struct trie *trie, const char *filename) { struct trie_f t = { .trie = trie, }; char *filename_tmp; int64_t pos; int64_t root_off; int64_t size; struct trie_header_f h = { .signature = HWDB_SIG, .tool_version = htole64(atoi(VERSION)), .header_size = htole64(sizeof(struct trie_header_f)), .node_size = htole64(sizeof(struct trie_node_f)), .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), .value_entry_size = htole64(sizeof(struct trie_value_entry_f)), }; int err; /* calculate size of header, nodes, children entries, value entries */ t.strings_off = sizeof(struct trie_header_f); trie_store_nodes_size(&t, trie->root); err = fopen_temporary(filename , &t.f, &filename_tmp); if (err < 0) return err; fchmod(fileno(t.f), 0444); /* write nodes */ fseeko(t.f, sizeof(struct trie_header_f), SEEK_SET); root_off = trie_store_nodes(&t, trie->root); h.nodes_root_off = htole64(root_off); pos = ftello(t.f); h.nodes_len = htole64(pos - sizeof(struct trie_header_f)); /* write string buffer */ fwrite(trie->strings->buf, trie->strings->len, 1, t.f); h.strings_len = htole64(trie->strings->len); /* write header */ size = ftello(t.f); h.file_size = htole64(size); fseeko(t.f, 0, SEEK_SET); fwrite(&h, sizeof(struct trie_header_f), 1, t.f); err = ferror(t.f); if (err) err = -errno; fclose(t.f); if (err < 0 || rename(filename_tmp, filename) < 0) { unlink(filename_tmp); goto out; } log_debug("=== trie on-disk ===\n"); log_debug("size: %8llu bytes\n", (unsigned long long)size); log_debug("header: %8zu bytes\n", sizeof(struct trie_header_f)); log_debug("nodes: %8llu bytes (%8llu)\n", (unsigned long long)t.nodes_count * sizeof(struct trie_node_f), (unsigned long long)t.nodes_count); log_debug("child pointers: %8llu bytes (%8llu)\n", (unsigned long long)t.children_count * sizeof(struct trie_child_entry_f), (unsigned long long)t.children_count); log_debug("value pointers: %8llu bytes (%8llu)\n", (unsigned long long)t.values_count * sizeof(struct trie_value_entry_f), (unsigned long long)t.values_count); log_debug("string store: %8llu bytes\n", (unsigned long long)trie->strings->len); log_debug("strings start: %8llu\n", (unsigned long long) t.strings_off); out: free(filename_tmp); return err; } static int import_file(struct trie *trie, const char *filename) { FILE *f; char line[LINE_MAX]; char match[LINE_MAX]; char cond[LINE_MAX]; f = fopen(filename, "re"); if (f == NULL) return -errno; match[0] = '\0'; cond[0] = '\0'; while (fgets(line, sizeof(line), f)) { size_t len; if (line[0] == '#') continue; /* new line, new record */ if (line[0] == '\n') { match[0] = '\0'; cond[0] = '\0'; continue; } /* remove newline */ len = strlen(line); if (len < 2) continue; line[len-1] = '\0'; /* start of new record */ if (match[0] == '\0') { strcpy(match, line); cond[0] = '\0'; continue; } if (line[0] == '+') { strcpy(cond, line); continue; } /* TODO: support +; skip the entire record until we support it */ if (cond[0] != '\0') continue; /* value lines */ if (line[0] == ' ') { char *value; value = strchr(line, '='); if (!value) continue; value[0] = '\0'; value++; trie_insert(trie, trie->root, match, line, value); } } fclose(f); return 0; } static void help(void) { printf("Usage: udevadm hwdb OPTIONS\n" " --update update the hardware database\n" " --test=<modalias> query database and print result\n" " --root=<path> alternative root path in the filesystem\n" " --help\n\n"); }