+ rules = malloc(sizeof(struct udev_rules));
+ if (rules == NULL)
+ return NULL;
+ memset(rules, 0x00, sizeof(struct udev_rules));
+ rules->udev = udev;
+ rules->resolve_names = resolve_names;
+ udev_list_init(&file_list);
+
+ /* init token array and string buffer */
+ rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token));
+ if (rules->tokens == NULL)
+ return NULL;
+ rules->token_max = PREALLOC_TOKEN;
+
+ rules->buf = malloc(PREALLOC_STRBUF);
+ if (rules->buf == NULL)
+ return NULL;
+ rules->buf_max = PREALLOC_STRBUF;
+ /* offset 0 is always '\0' */
+ rules->buf[0] = '\0';
+ rules->buf_cur = 1;
+ dbg(udev, "prealloc %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n",
+ rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max);
+
+ rules->trie_nodes = malloc(PREALLOC_TRIE * sizeof(struct trie_node));
+ if (rules->trie_nodes == NULL)
+ return NULL;
+ rules->trie_nodes_max = PREALLOC_TRIE;
+ /* offset 0 is the trie root, with an empty string */
+ memset(rules->trie_nodes, 0x00, sizeof(struct trie_node));
+ rules->trie_nodes_cur = 1;
+
+ if (udev_get_rules_path(udev) != NULL) {
+ /* custom rules location for testing */
+ add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules");
+ } else {
+ char filename[PATH_MAX];
+ struct udev_list_node sort_list;
+ struct udev_list_entry *sort_loop, *sort_tmp;
+
+ /* read user/custom rules */
+ add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".rules");
+
+ /* read dynamic/temporary rules */
+ util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename));
+ util_strlcat(filename, "/.udev/rules.d", sizeof(filename));
+ if (stat(filename, &statbuf) != 0) {
+ util_create_path(udev, filename);
+ udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755);
+ mkdir(filename, 0755);
+ udev_selinux_resetfscreatecon(udev);
+ }
+ udev_list_init(&sort_list);
+ add_matching_files(udev, &sort_list, filename, ".rules");
+
+ /* read default rules */
+ add_matching_files(udev, &sort_list, UDEV_PREFIX "/lib/udev/rules.d", ".rules");
+
+ /* sort all rules files by basename into list of files */
+ udev_list_entry_foreach_safe(sort_loop, sort_tmp, udev_list_get_entry(&sort_list)) {
+ const char *sort_name = udev_list_entry_get_name(sort_loop);
+ const char *sort_base = strrchr(sort_name, '/');
+
+ if (sort_base == NULL)
+ continue;
+ /* sort entry into existing list */
+ udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
+ const char *file_name = udev_list_entry_get_name(file_loop);
+ const char *file_base = strrchr(file_name, '/');
+
+ if (file_base == NULL)
+ continue;
+ if (strcmp(file_base, sort_base) == 0) {
+ info(udev, "rule file basename '%s' already added, ignoring '%s'\n",
+ file_name, sort_name);
+ udev_list_entry_delete(sort_loop);
+ sort_loop = NULL;
+ break;
+ }
+ if (strcmp(file_base, sort_base) > 0) {
+ /* found later file, insert before */
+ udev_list_entry_remove(sort_loop);
+ udev_list_entry_insert_before(sort_loop, file_loop);
+ sort_loop = NULL;
+ break;
+ }
+ }
+ /* current file already handled */
+ if (sort_loop == NULL)
+ continue;
+ /* no later file, append to end of list */
+ udev_list_entry_remove(sort_loop);
+ udev_list_entry_append(sort_loop, &file_list);