]> git.ipfire.org Git - thirdparty/kmod.git/blobdiff - tools/depmod.c
depmod: fix leak on error path
[thirdparty/kmod.git] / tools / depmod.c
index f2f0b77c367aa1886c413d2c545b770fea1a043a..116adbeb14a0f53aa1821e446518c52cbce17be1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * kmod-depmod - calculate modules.dep  using libkmod.
  *
- * Copyright (C) 2011-2012  ProFUSION embedded systems
+ * Copyright (C) 2011-2013  ProFUSION embedded systems
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-#include "libkmod.h"
-#include "libkmod-array.h"
-#include "libkmod-hash.h"
-#include "libkmod-util.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
-#include <string.h>
-#include <syslog.h>
+#include <getopt.h>
 #include <limits.h>
-#include <dirent.h>
-#include <sys/utsname.h>
-#include <sys/stat.h>
 #include <regex.h>
-#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
-#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
 
-#define DEFAULT_VERBOSE LOG_WARNING
-static int verbose = DEFAULT_VERBOSE;
+#include <shared/array.h>
+#include <shared/hash.h>
+#include <shared/macro.h>
+#include <shared/util.h>
+#include <shared/scratchbuf.h>
 
-#define KMOD_EXT_UNC 0
+#include <libkmod/libkmod-internal.h>
 
-static const struct kmod_ext {
-       const char *ext;
-       size_t len;
-} kmod_exts[] = {
-       {".ko", sizeof(".ko") - 1},
-#ifdef ENABLE_ZLIB
-       {".ko.gz", sizeof(".ko.gz") - 1},
-#endif
-#ifdef ENABLE_XZ
-       {".ko.xz", sizeof(".ko.xz") - 1},
-#endif
-       {NULL, 0},
-};
+#undef ERR
+#undef DBG
+
+#include "kmod.h"
+
+#define DEFAULT_VERBOSE LOG_WARNING
+static int verbose = DEFAULT_VERBOSE;
 
 static const char CFG_BUILTIN_KEY[] = "built-in";
 static const char *default_cfg_paths[] = {
@@ -78,7 +70,7 @@ static const struct option cmdopts[] = {
        { "verbose", no_argument, 0, 'v' },
        { "show", no_argument, 0, 'n' },
        { "dry-run", no_argument, 0, 'n' },
-       { "symbol-prefix", no_argument, 0, 'P' },
+       { "symbol-prefix", required_argument, 0, 'P' },
        { "warn", no_argument, 0, 'w' },
        { "map", no_argument, 0, 'm' }, /* deprecated */
        { "version", no_argument, 0, 'V' },
@@ -86,10 +78,9 @@ static const struct option cmdopts[] = {
        { }
 };
 
-static void help(const char *progname)
+static void help(void)
 {
-       fprintf(stderr,
-               "Usage:\n"
+       printf("Usage:\n"
                "\t%s -[aA] [options] [forced_version]\n"
                "\n"
                "If no arguments (except options) are given, \"depmod -a\" is assumed\n"
@@ -114,9 +105,10 @@ static void help(const char *progname)
                "\t                     current kernel symbols.\n"
                "\t-E, --symvers=FILE   Use Module.symvers file to check\n"
                "\t                     symbol versions.\n",
-               progname);
+               program_invocation_short_name);
 }
 
+_printf_format_(1, 2)
 static inline void _show(const char *fmt, ...)
 {
        va_list args;
@@ -129,64 +121,11 @@ static inline void _show(const char *fmt, ...)
        fflush(stdout);
        va_end(args);
 }
-
-static void _log(int prio, const char *fmt, ...)
-{
-       const char *prioname;
-       char buf[32], *msg;
-       va_list args;
-
-       if (prio > verbose)
-               return;
-
-       va_start(args, fmt);
-       if (vasprintf(&msg, fmt, args) < 0)
-               msg = NULL;
-       va_end(args);
-       if (msg == NULL)
-               return;
-
-       switch (prio) {
-       case LOG_CRIT:
-               prioname = "FATAL";
-               break;
-       case LOG_ERR:
-               prioname = "ERROR";
-               break;
-       case LOG_WARNING:
-               prioname = "WARNING";
-               break;
-       case LOG_NOTICE:
-               prioname = "NOTICE";
-               break;
-       case LOG_INFO:
-               prioname = "INFO";
-               break;
-       case LOG_DEBUG:
-               prioname = "DEBUG";
-               break;
-       default:
-               snprintf(buf, sizeof(buf), "LOG-%03d", prio);
-               prioname = buf;
-       }
-
-       fprintf(stderr, "%s: %s", prioname, msg);
-       free(msg);
-
-       if (prio <= LOG_CRIT)
-               exit(EXIT_FAILURE);
-}
-#define CRIT(...) _log(LOG_CRIT, __VA_ARGS__)
-#define ERR(...) _log(LOG_ERR, __VA_ARGS__)
-#define WRN(...) _log(LOG_WARNING, __VA_ARGS__)
-#define INF(...) _log(LOG_INFO, __VA_ARGS__)
-#define DBG(...) _log(LOG_DEBUG, __VA_ARGS__)
 #define SHOW(...) _show(__VA_ARGS__)
 
 
 /* binary index write *************************************************/
 #include <arpa/inet.h>
-#include "macro.h"
 /* BEGIN: code from module-init-tools/index.c just modified to compile here.
  *
  * Original copyright:
@@ -207,84 +146,13 @@ static void _log(int prio, const char *fmt, ...)
  *   along with these programs.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* Integers are stored as 32 bit unsigned in "network" order, i.e. MSB first.
-   All files start with a magic number.
+/* see documentation in libkmod/libkmod-index.c */
 
-   Magic spells "BOOTFAST". Second one used on newer versioned binary files.
- */
-/* #define INDEX_MAGIC_OLD 0xB007FA57 */
 #define INDEX_MAGIC 0xB007F457
-
-/* We use a version string to keep track of changes to the binary format
- * This is stored in the form: INDEX_MAJOR (hi) INDEX_MINOR (lo) just in
- * case we ever decide to have minor changes that are not incompatible.
- */
-
 #define INDEX_VERSION_MAJOR 0x0002
 #define INDEX_VERSION_MINOR 0x0001
 #define INDEX_VERSION ((INDEX_VERSION_MAJOR<<16)|INDEX_VERSION_MINOR)
-
-/* The index file maps keys to values. Both keys and values are ASCII strings.
-   Each key can have multiple values. Values are sorted by an integer priority.
-
-   The reader also implements a wildcard search (including range expressions)
-   where the keys in the index are treated as patterns.
-   This feature is required for module aliases.
-*/
-
-/* Implementation is based on a radix tree, or "trie".
-   Each arc from parent to child is labelled with a character.
-   Each path from the root represents a string.
-
-   == Example strings ==
-
-   ask
-   ate
-   on
-   once
-   one
-
-   == Key ==
-    + Normal node
-    * Marked node, representing a key and it's values.
-
-   +
-   |-a-+-s-+-k-*
-   |   |
-   |   `-t-+-e-*
-   |
-   `-o-+-n-*-c-+-e-*
-           |
-           `-e-*
-
-   Naive implementations tend to be very space inefficient; child pointers
-   are stored in arrays indexed by character, but most child pointers are null.
-
-   Our implementation uses a scheme described by Wikipedia as a Patrica trie,
-
-       "easiest to understand as a space-optimized trie where
-        each node with only one child is merged with its child"
-
-   +
-   |-a-+-sk-*
-   |   |
-   |   `-te-*
-   |
-   `-on-*-ce-*
-        |
-        `-e-*
-
-   We still use arrays of child pointers indexed by a single character;
-   the remaining characters of the label are stored as a "prefix" in the child.
-
-   The paper describing the original Patrica trie works on individiual bits -
-   each node has a maximum of two children, which increases space efficiency.
-   However for this application it is simpler to use the ASCII character set.
-   Since the index file is read-only, it can be compressed by omitting null
-   child pointers at the start and end of arrays.
-*/
-
-#define INDEX_PRIORITY_MIN UINT32_MAX
+#define INDEX_CHILDMAX 128
 
 struct index_value {
        struct index_value *next;
@@ -293,8 +161,6 @@ struct index_value {
 };
 
 /* In-memory index (depmod only) */
-
-#define INDEX_CHILDMAX 128
 struct index_node {
        char *prefix;           /* path compression */
        struct index_value *values;
@@ -303,32 +169,6 @@ struct index_node {
        struct index_node *children[INDEX_CHILDMAX]; /* indexed by character */
 };
 
-/* Disk format:
-
-   uint32_t magic = INDEX_MAGIC;
-   uint32_t version = INDEX_VERSION;
-   uint32_t root_offset;
-
-   (node_offset & INDEX_NODE_MASK) specifies the file offset of nodes:
-
-        char[] prefix; // nul terminated
-
-        char first;
-        char last;
-        uint32_t children[last - first + 1];
-
-        uint32_t value_count;
-        struct {
-            uint32_t priority;
-            char[] value; // nul terminated
-        } values[value_count];
-
-   (node_offset & INDEX_NODE_FLAGS) indicates which fields are present.
-   Empty prefixes are omitted, leaf nodes omit the three child-related fields.
-
-   This could be optimised further by adding a sparse child format
-   (indicated using a new flag).
- */
 
 /* Format of node offsets within index file */
 enum node_offset {
@@ -531,10 +371,11 @@ static uint32_t index_write__node(const struct index_node *node, FILE *out)
                fputc(node->first, out);
                fputc(node->last, out);
                fwrite(child_offs, sizeof(uint32_t), child_count, out);
-               free(child_offs);
                offset |= INDEX_NODE_CHILDS;
        }
 
+       free(child_offs);
+
        if (node->values) {
                const struct index_value *v;
                unsigned int value_count;
@@ -570,6 +411,7 @@ static void index_write(const struct index_node *node, FILE *out)
 
        /* Second word is reserved for the offset of the root node */
        initial_offset = ftell(out);
+       assert(initial_offset >= 0);
        u = 0;
        fwrite(&u, sizeof(uint32_t), 1, out);
 
@@ -578,49 +420,15 @@ static void index_write(const struct index_node *node, FILE *out)
 
        /* Update first word */
        final_offset = ftell(out);
-       fseek(out, initial_offset, SEEK_SET);
+       assert(final_offset >= 0);
+       (void)fseek(out, initial_offset, SEEK_SET);
        fwrite(&u, sizeof(uint32_t), 1, out);
-       fseek(out, final_offset, SEEK_SET);
+       (void)fseek(out, final_offset, SEEK_SET);
 }
 
 /* END: code from module-init-tools/index.c just modified to compile here.
  */
 
-/* utils (variants of libkmod-utils.c) *********************************/
-static const char *underscores2(const char *input, char *output, size_t outputlen)
-{
-       size_t i;
-
-       for (i = 0; input[i] != '\0' && i < outputlen - 1; i++) {
-               switch (input[i]) {
-               case '-':
-                       output[i] = '_';
-                       break;
-
-               case ']':
-                       WRN("Unmatched bracket in %s\n", input);
-                       return NULL;
-
-               case '[': {
-                       size_t off = strcspn(input + i, "]");
-                       if (input[i + off] == '\0') {
-                               WRN("Unmatched bracket in %s\n", input);
-                               return NULL;
-                       }
-                       memcpy(output + i, input + i, off + 1);
-                       i += off;
-                       break;
-               }
-
-               default:
-                       output[i] = input[i];
-               }
-       }
-       output[i] = '\0';
-
-       return output;
-}
-
 /* configuration parsing **********************************************/
 struct cfg_override {
        struct cfg_override *next;
@@ -750,7 +558,7 @@ static int cfg_file_parse(struct cfg *cfg, const char *filename)
                return err;
        }
 
-       while ((line = getline_wrapped(fp, &linenum)) != NULL) {
+       while ((line = freadline_wrapped(fp, &linenum)) != NULL) {
                char *cmd, *saveptr;
 
                if (line[0] == '\0' || line[0] == '#')
@@ -902,6 +710,7 @@ static int cfg_files_insert_sorted(struct cfg_file ***p_files, size_t *p_n_files
 static int cfg_files_list(struct cfg_file ***p_files, size_t *p_n_files,
                                const char *path)
 {
+       struct dirent *dent;
        DIR *d;
        int err = 0;
        struct stat st;
@@ -912,12 +721,9 @@ static int cfg_files_list(struct cfg_file ***p_files, size_t *p_n_files,
                return err;
        }
 
-       if (S_ISREG(st.st_mode)) {
+       if (!S_ISDIR(st.st_mode)) {
                cfg_files_insert_sorted(p_files, p_n_files, path, NULL);
                return 0;
-       } if (!S_ISDIR(st.st_mode)) {
-               ERR("unsupported file mode %s: %#x\n", path, st.st_mode);
-               return -EINVAL;
        }
 
        d = opendir(path);
@@ -926,20 +732,11 @@ static int cfg_files_list(struct cfg_file ***p_files, size_t *p_n_files,
                return -EINVAL;
        }
 
-       for (;;) {
-               struct dirent ent, *entp;
-
-               err = readdir_r(d, &ent, &entp);
-               if (err != 0) {
-                       ERR("reading entry %s\n", strerror(-err));
-                       break;
-               }
-               if (entp == NULL)
-                       break;
-               if (cfg_files_filter_out(d, path, entp->d_name))
+       for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
+               if (cfg_files_filter_out(d, path, dent->d_name))
                        continue;
 
-               cfg_files_insert_sorted(p_files, p_n_files, path, entp->d_name);
+               cfg_files_insert_sorted(p_files, p_n_files, path, dent->d_name);
        }
 
        closedir(d);
@@ -991,6 +788,7 @@ static void cfg_free(struct cfg *cfg)
 
 
 /* depmod calculations ***********************************************/
+struct vertex;
 struct mod {
        struct kmod_module *kmod;
        char *path;
@@ -1000,12 +798,13 @@ struct mod {
        struct kmod_list *dep_sym_list;
        struct array deps; /* struct symbol */
        size_t baselen; /* points to start of basename/filename */
-       size_t modnamelen;
+       size_t modnamesz;
        int sort_idx; /* sort index using modules.order */
        int dep_sort_idx; /* topological sort index */
        uint16_t idx; /* index in depmod->modules.array */
        uint16_t users; /* how many modules depend on this one */
-       uint8_t dep_loop : 1;
+       bool visited; /* helper field to report cycles */
+       struct vertex *vertex; /* helper field to report cycles */
        char modname[];
 };
 
@@ -1022,14 +821,13 @@ struct depmod {
        struct hash *modules_by_uncrelpath;
        struct hash *modules_by_name;
        struct hash *symbols;
-       unsigned int dep_loops;
 };
 
 static void mod_free(struct mod *mod)
 {
        DBG("free %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path);
        array_free_array(&mod->deps);
-       assert(mod->kmod == NULL);
+       kmod_module_unref(mod->kmod);
        kmod_module_info_free_list(mod->info_list);
        kmod_module_dependency_symbols_free_list(mod->dep_sym_list);
        free(mod->uncrelpath);
@@ -1124,21 +922,21 @@ static int depmod_module_add(struct depmod *depmod, struct kmod_module *kmod)
 {
        const struct cfg *cfg = depmod->cfg;
        const char *modname, *lastslash;
-       size_t modnamelen;
+       size_t modnamesz;
        struct mod *mod;
        int err;
 
        modname = kmod_module_get_name(kmod);
-       modnamelen = strlen(modname) + 1;
+       modnamesz = strlen(modname) + 1;
 
-       mod = calloc(1, sizeof(struct mod) + modnamelen);
+       mod = calloc(1, sizeof(struct mod) + modnamesz);
        if (mod == NULL)
                return -ENOMEM;
        mod->kmod = kmod;
        mod->sort_idx = depmod->modules.count + 1;
        mod->dep_sort_idx = INT32_MAX;
-       memcpy(mod->modname, modname, modnamelen);
-       mod->modnamelen = modnamelen;
+       memcpy(mod->modname, modname, modnamesz);
+       mod->modnamesz = modnamesz;
 
        array_init(&mod->deps, 4);
 
@@ -1158,15 +956,15 @@ static int depmod_module_add(struct depmod *depmod, struct kmod_module *kmod)
        }
 
        if (mod->relpath != NULL) {
-               size_t uncrelpathlen = lastslash - mod->relpath + modnamelen
-                                      + kmod_exts[KMOD_EXT_UNC].len;
+               size_t uncrelpathlen = lastslash - mod->relpath + modnamesz
+                                      + strlen(KMOD_EXTENSION_UNCOMPRESSED);
                mod->uncrelpath = memdup(mod->relpath, uncrelpathlen + 1);
                mod->uncrelpath[uncrelpathlen] = '\0';
                err = hash_add_unique(depmod->modules_by_uncrelpath,
                                      mod->uncrelpath, mod);
                if (err < 0) {
                        ERR("hash_add_unique %s: %s\n",
-                           mod->relpath, strerror(-err));
+                           mod->uncrelpath, strerror(-err));
                        hash_del(depmod->modules_by_name, mod->modname);
                        goto fail;
                }
@@ -1186,8 +984,8 @@ static int depmod_module_del(struct depmod *depmod, struct mod *mod)
 {
        DBG("del %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path);
 
-       if (mod->relpath != NULL)
-               hash_del(depmod->modules_by_uncrelpath, mod->relpath);
+       if (mod->uncrelpath != NULL)
+               hash_del(depmod->modules_by_uncrelpath, mod->uncrelpath);
 
        hash_del(depmod->modules_by_name, mod->modname);
 
@@ -1203,8 +1001,12 @@ static int depmod_module_is_higher_priority(const struct depmod *depmod, const s
        const struct cfg *cfg = depmod->cfg;
        const struct cfg_override *ov;
        const struct cfg_search *se;
+
+       /* baselen includes the last '/' and mod->baselen doesn't. So it's
+        * actually correct to use modnamelen in the first and modnamesz in
+        * the latter */
        size_t newlen = baselen + modnamelen;
-       size_t oldlen = mod->baselen + mod->modnamelen;
+       size_t oldlen = mod->baselen + mod->modnamesz;
        const char *oldpath = mod->path;
        int i, bprio = -1, oldprio = -1, newprio = -1;
 
@@ -1231,10 +1033,10 @@ static int depmod_module_is_higher_priority(const struct depmod *depmod, const s
                DBG("search %s\n", se->builtin ? "built-in" : se->path);
                if (se->builtin)
                        bprio = i;
-               else if (newlen >= se->len &&
+               else if (newlen > se->len && newpath[se->len] == '/' &&
                         memcmp(se->path, newpath, se->len) == 0)
                        newprio = i;
-               else if (oldlen >= se->len &&
+               else if (oldlen > se->len && oldpath[se->len] == '/' &&
                         memcmp(se->path, oldpath, se->len) == 0)
                        oldprio = i;
        }
@@ -1245,7 +1047,7 @@ static int depmod_module_is_higher_priority(const struct depmod *depmod, const s
                oldprio = bprio;
 
        DBG("priorities: built-in: %d, old: %d, new: %d\n",
-           bprio, newprio, oldprio);
+           bprio, oldprio, newprio);
 
        return newprio <= oldprio;
 }
@@ -1256,20 +1058,10 @@ static int depmod_modules_search_file(struct depmod *depmod, size_t baselen, siz
        struct mod *mod;
        const char *relpath;
        char modname[PATH_MAX];
-       const struct kmod_ext *eitr;
        size_t modnamelen;
-       uint8_t matches = 0;
        int err;
 
-       for (eitr = kmod_exts; eitr->ext != NULL; eitr++) {
-               if (namelen <= eitr->len)
-                       continue;
-               if (streq(path + baselen + namelen - eitr->len, eitr->ext)) {
-                       matches = 1;
-                       break;
-               }
-       }
-       if (!matches)
+       if (!path_ends_with_kmod_ext(path + baselen, namelen))
                return 0;
 
        if (path_to_modname(path, modname, &modnamelen) == NULL) {
@@ -1334,7 +1126,7 @@ static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t basel
                namelen = strlen(name);
                if (baselen + namelen + 2 >= PATH_MAX) {
                        path[baselen] = '\0';
-                       ERR("path is too long %s%s %zd\n", path, name);
+                       ERR("path is too long %s%s\n", path, name);
                        continue;
                }
                memcpy(path + baselen, name, namelen + 1);
@@ -1425,12 +1217,6 @@ static int depmod_modules_search(struct depmod *depmod)
 static int mod_cmp(const void *pa, const void *pb) {
        const struct mod *a = *(const struct mod **)pa;
        const struct mod *b = *(const struct mod **)pb;
-       if (a->dep_loop == b->dep_loop)
-               return a->sort_idx - b->sort_idx;
-       else if (a->dep_loop)
-               return 1;
-       else if (b->dep_loop)
-               return -1;
        return a->sort_idx - b->sort_idx;
 }
 
@@ -1506,13 +1292,14 @@ corrupted:
 }
 
 static int depmod_symbol_add(struct depmod *depmod, const char *name,
-                                       uint64_t crc, const struct mod *owner)
+                                       bool prefix_skipped, uint64_t crc,
+                                       const struct mod *owner)
 {
        size_t namelen;
        int err;
        struct symbol *sym;
 
-       if (name[0] == depmod->cfg->sym_prefix)
+       if (!prefix_skipped && (name[0] == depmod->cfg->sym_prefix))
                name++;
 
        namelen = strlen(name) + 1;
@@ -1569,7 +1356,7 @@ static int depmod_load_modules(struct depmod *depmod)
                kmod_list_foreach(l, list) {
                        const char *name = kmod_module_symbol_get_symbol(l);
                        uint64_t crc = kmod_module_symbol_get_crc(l);
-                       depmod_symbol_add(depmod, name, crc, mod);
+                       depmod_symbol_add(depmod, name, false, crc, mod);
                }
                kmod_module_symbols_free_list(list);
 
@@ -1581,7 +1368,7 @@ load_info:
                mod->kmod = NULL;
        }
 
-       DBG("loaded symbols (%zd modules, %zd symbols)\n",
+       DBG("loaded symbols (%zd modules, %u symbols)\n",
            depmod->modules.count, hash_get_count(depmod->symbols));
 
        return 0;
@@ -1627,7 +1414,7 @@ static int depmod_load_dependencies(struct depmod *depmod)
 {
        struct mod **itr, **itr_end;
 
-       DBG("load dependencies (%zd modules, %zd symbols)\n",
+       DBG("load dependencies (%zd modules, %u symbols)\n",
            depmod->modules.count, hash_get_count(depmod->symbols));
 
        itr = (struct mod **)depmod->modules.array;
@@ -1643,7 +1430,7 @@ static int depmod_load_dependencies(struct depmod *depmod)
                depmod_load_module_dependencies(depmod, mod);
        }
 
-       DBG("loaded dependencies (%zd modules, %zd symbols)\n",
+       DBG("loaded dependencies (%zd modules, %u symbols)\n",
            depmod->modules.count, hash_get_count(depmod->symbols));
 
        return 0;
@@ -1653,12 +1440,6 @@ static int dep_cmp(const void *pa, const void *pb)
 {
        const struct mod *a = *(const struct mod **)pa;
        const struct mod *b = *(const struct mod **)pb;
-       if (a->dep_loop == b->dep_loop)
-               return a->dep_sort_idx - b->dep_sort_idx;
-       else if (a->dep_loop)
-               return 1;
-       else if (b->dep_loop)
-               return -1;
        return a->dep_sort_idx - b->dep_sort_idx;
 }
 
@@ -1674,11 +1455,237 @@ static void depmod_sort_dependencies(struct depmod *depmod)
        }
 }
 
+struct vertex {
+       struct vertex *parent;
+       struct mod *mod;
+};
+
+static struct vertex *vertex_new(struct mod *mod, struct vertex *parent)
+{
+       struct vertex *v;
+
+       v = malloc(sizeof(*v));
+       if (v == NULL)
+               return NULL;
+
+       v->parent = parent;
+       v->mod = mod;
+       return v;
+}
+
+static void depmod_list_remove_data(struct kmod_list **list, void *data)
+{
+       struct kmod_list *l;
+
+       l = kmod_list_remove_data(*list, data);
+       *list = l;
+}
+
+static void depmod_report_one_cycle(struct depmod *depmod,
+                                   struct vertex *vertex,
+                                   struct kmod_list **roots,
+                                   struct hash *loop_set)
+{
+       const char sep[] = " -> ";
+       size_t sz;
+       char *buf;
+       struct array reverse;
+       int i;
+       int n;
+       struct vertex *v;
+
+       array_init(&reverse, 3);
+
+       sz = 0;
+       for (v = vertex->parent, n = 0;
+            v != NULL;
+            v = v->parent, n++) {
+
+               sz += v->mod->modnamesz - 1;
+               array_append(&reverse, v);
+               hash_add(loop_set, v->mod->modname, NULL);
+       }
+       sz += vertex->mod->modnamesz - 1;
+
+       buf = malloc(sz + n * strlen(sep) + 1);
+
+       sz = 0;
+       for (i = reverse.count - 1; i >= 0; i--) {
+               size_t len;
+
+               v = reverse.array[i];
+
+               len = v->mod->modnamesz - 1;
+               memcpy(buf + sz, v->mod->modname, len);
+               sz += len;
+               strcpy(buf + sz, sep);
+               sz += strlen(sep);
+
+               depmod_list_remove_data(roots, v->mod);
+       }
+       strcpy(buf + sz, vertex->mod->modname);
+       ERR("Cycle detected: %s\n", buf);
+
+       free(buf);
+       array_free_array(&reverse);
+}
+
+static int depmod_report_cycles_from_root(struct depmod *depmod,
+                                         struct mod *root_mod,
+                                         struct kmod_list **roots,
+                                         void **stack,
+                                         size_t stack_size,
+                                         struct hash *loop_set)
+{
+       struct kmod_list *free_list = NULL; /* struct vertex */
+       struct kmod_list *l;
+       struct vertex *root;
+       struct vertex *vertex;
+       struct vertex *v;
+       struct mod *m;
+       struct mod **itr, **itr_end;
+       size_t is;
+
+       root = vertex_new(root_mod, NULL);
+       if (root == NULL) {
+               ERR("No memory to report cycles\n");
+               return -ENOMEM;
+       }
+
+       l = kmod_list_append(free_list, root);
+       if (l == NULL) {
+               ERR("No memory to report cycles\n");
+               return -ENOMEM;
+       }
+       free_list = l;
+
+       is = 0;
+       stack[is++] = (void *)root;
+
+       while (is > 0) {
+               vertex = stack[--is];
+               m = vertex->mod;
+               /*
+                * because of the topological sort we can start only
+                * from part of a loop or from a branch after a loop
+                */
+               if (m->visited && m == root->mod) {
+                       depmod_report_one_cycle(depmod, vertex,
+                                               roots, loop_set);
+                       continue;
+               }
+
+               m->visited = true;
+               if (m->deps.count == 0) {
+                       /*
+                        * boundary condition: if there is more than one
+                        * single node branch (not a loop), it is
+                        * recognized as a loop by the code above:
+                        * m->visited because more then one,
+                        * m == root->mod since it is a single node.
+                        * So, prevent deeping into the branch second
+                        * time.
+                        */
+                       depmod_list_remove_data(roots, m);
+
+                       continue;
+               }
+
+               itr = (struct mod **) m->deps.array;
+               itr_end = itr + m->deps.count;
+               for (; itr < itr_end; itr++) {
+                       struct mod *dep = *itr;
+                       v = vertex_new(dep, vertex);
+                       if (v == NULL) {
+                               ERR("No memory to report cycles\n");
+                               return -ENOMEM;
+                       }
+                       assert(is < stack_size);
+                       stack[is++] = v;
+
+                       l = kmod_list_append(free_list, v);
+                       if (l == NULL) {
+                               ERR("No memory to report cycles\n");
+                               return -ENOMEM;
+                       }
+                       free_list = l;
+
+               }
+       }
+       while (free_list) {
+               v = free_list->data;
+               l = kmod_list_remove(free_list);
+               free_list = l;
+               free(v);
+       }
+
+       return 0;
+}
+
+static void depmod_report_cycles(struct depmod *depmod, uint16_t n_mods,
+                                uint16_t *users)
+{
+       int num_cyclic = 0;
+       struct kmod_list *roots = NULL; /* struct mod */
+       struct kmod_list *l;
+       size_t n_r; /* local n_roots */
+       int i;
+       int err;
+       _cleanup_free_ void **stack = NULL;
+       struct mod *m;
+       struct mod *root;
+       struct hash *loop_set;
+
+       for (i = 0, n_r = 0; i < n_mods; i++) {
+               if (users[i] <= 0)
+                       continue;
+               m = depmod->modules.array[i];
+               l = kmod_list_append(roots, m);
+               if (l == NULL) {
+                       ERR("No memory to report cycles\n");
+                       return;
+               }
+               roots = l;
+               n_r++;
+       }
+
+       stack = malloc(n_r * sizeof(void *));
+       if (stack == NULL) {
+               ERR("No memory to report cycles\n");
+               return;
+       }
+
+       loop_set = hash_new(16, NULL);
+       if (loop_set == NULL) {
+               ERR("No memory to report cycles\n");
+               return;
+       }
+
+       while (roots != NULL) {
+               root = roots->data;
+               l = kmod_list_remove(roots);
+               roots = l;
+               err = depmod_report_cycles_from_root(depmod,
+                                                    root,
+                                                    &roots,
+                                                    stack, n_r, loop_set);
+               if (err < 0)
+                       goto err;
+       }
+
+       num_cyclic = hash_get_count(loop_set);
+       ERR("Found %d modules in dependency cycles!\n", num_cyclic);
+
+err:
+       hash_free(loop_set);
+}
+
 static int depmod_calculate_dependencies(struct depmod *depmod)
 {
        const struct mod **itrm;
        uint16_t *users, *roots, *sorted;
        uint16_t i, n_roots = 0, n_sorted = 0, n_mods = depmod->modules.count;
+       int ret = 0;
 
        users = malloc(sizeof(uint16_t) * n_mods * 3);
        if (users == NULL)
@@ -1686,7 +1693,7 @@ static int depmod_calculate_dependencies(struct depmod *depmod)
        roots = users + n_mods;
        sorted = roots + n_mods;
 
-       DBG("calculate dependencies and ordering (%zd modules)\n", n_mods);
+       DBG("calculate dependencies and ordering (%hu modules)\n", n_mods);
 
        assert(depmod->modules.count < UINT16_MAX);
 
@@ -1727,27 +1734,18 @@ static int depmod_calculate_dependencies(struct depmod *depmod)
        }
 
        if (n_sorted < n_mods) {
-               WRN("found %hu modules in dependency cycles!\n",
-                   n_mods - n_sorted);
-               for (i = 0; i < n_mods; i++) {
-                       struct mod *m;
-                       if (users[i] == 0)
-                               continue;
-                       m = depmod->modules.array[i];
-                       WRN("%s in dependency cycle!\n", m->path);
-                       m->dep_loop = 1;
-                       m->dep_sort_idx = INT32_MAX;
-                       depmod->dep_loops++;
-               }
+               depmod_report_cycles(depmod, n_mods, users);
+               ret = -EINVAL;
+               goto exit;
        }
 
        depmod_sort_dependencies(depmod);
 
-       DBG("calculated dependencies and ordering (%u loops, %zd modules)\n",
-           depmod->dep_loops, n_mods);
+       DBG("calculated dependencies and ordering (%hu modules)\n", n_mods);
 
+exit:
        free(users);
-       return 0;
+       return ret;
 }
 
 static int depmod_load(struct depmod *depmod)
@@ -1848,11 +1846,6 @@ static int output_deps(struct depmod *depmod, FILE *out)
                const char *p = mod_get_compressed_path(mod);
                size_t j, n_deps;
 
-               if (mod->dep_loop) {
-                       DBG("Ignored %s due dependency loops\n", p);
-                       continue;
-               }
-
                fprintf(out, "%s:", p);
 
                if (mod->deps.count == 0)
@@ -1866,12 +1859,6 @@ static int output_deps(struct depmod *depmod, FILE *out)
 
                for (j = 0; j < n_deps; j++) {
                        const struct mod *d = deps[j];
-                       if (d->dep_loop) {
-                               DBG("Ignored %s (dependency of %s) "
-                                   "due dependency loops\n",
-                                   mod_get_compressed_path(d), p);
-                               continue;
-                       }
                        fprintf(out, " %s", mod_get_compressed_path(d));
                }
                free(deps);
@@ -1901,11 +1888,6 @@ static int output_deps_bin(struct depmod *depmod, FILE *out)
                size_t j, n_deps, linepos, linelen, slen;
                int duplicate;
 
-               if (mod->dep_loop) {
-                       DBG("Ignored %s due dependency loops\n", p);
-                       continue;
-               }
-
                deps = mod_get_all_sorted_dependencies(mod, &n_deps);
                if (deps == NULL && n_deps > 0) {
                        ERR("could not get all sorted dependencies of %s\n", p);
@@ -1915,12 +1897,6 @@ static int output_deps_bin(struct depmod *depmod, FILE *out)
                linelen = strlen(p) + 1;
                for (j = 0; j < n_deps; j++) {
                        const struct mod *d = deps[j];
-                       if (d->dep_loop) {
-                               DBG("Ignored %s (dependency of %s) "
-                                   "due dependency loops\n",
-                                   mod_get_compressed_path(d), p);
-                               continue;
-                       }
                        linelen += 1 + strlen(mod_get_compressed_path(d));
                }
 
@@ -1941,8 +1917,7 @@ static int output_deps_bin(struct depmod *depmod, FILE *out)
                for (j = 0; j < n_deps; j++) {
                        const struct mod *d = deps[j];
                        const char *dp;
-                       if (d->dep_loop)
-                               continue;
+
                        line[linepos] = ' ';
                        linepos++;
 
@@ -1992,7 +1967,6 @@ static int output_aliases(struct depmod *depmod, FILE *out)
 
 static int output_aliases_bin(struct depmod *depmod, FILE *out)
 {
-       char buf[1024];
        struct index_node *idx;
        size_t i;
 
@@ -2010,15 +1984,18 @@ static int output_aliases_bin(struct depmod *depmod, FILE *out)
                kmod_list_foreach(l, mod->info_list) {
                        const char *key = kmod_module_info_get_key(l);
                        const char *value = kmod_module_info_get_value(l);
+                       char buf[PATH_MAX];
                        const char *alias;
                        int duplicate;
 
                        if (!streq(key, "alias"))
                                continue;
 
-                       alias = underscores2(value, buf, sizeof(buf));
-                       if (alias == NULL)
+                       if (alias_normalize(value, buf, NULL) < 0) {
+                               WRN("Unmatched bracket in %s\n", value);
                                continue;
+                       }
+                       alias = buf;
 
                        duplicate = index_insert(idx, alias, mod->modname,
                                                 mod->idx);
@@ -2039,8 +2016,6 @@ static int output_softdeps(struct depmod *depmod, FILE *out)
        size_t i;
 
        fputs("# Soft dependencies extracted from modules themselves.\n", out);
-       fputs("# Copy, with a .conf extension, to /etc/modprobe.d to use "
-             "it with modprobe.\n", out);
 
        for (i = 0; i < depmod->modules.count; i++) {
                const struct mod *mod = depmod->modules.array[i];
@@ -2085,9 +2060,12 @@ static int output_symbols_bin(struct depmod *depmod, FILE *out)
 {
        struct index_node *idx;
        char alias[1024];
+       _cleanup_(scratchbuf_release) struct scratchbuf salias =
+               SCRATCHBUF_INITIALIZER(alias);
        size_t baselen = sizeof("symbol:") - 1;
        struct hash_iter iter;
        const void *v;
+       int ret = 0;
 
        if (out == stdout)
                return 0;
@@ -2097,16 +2075,24 @@ static int output_symbols_bin(struct depmod *depmod, FILE *out)
                return -ENOMEM;
 
        memcpy(alias, "symbol:", baselen);
+
        hash_iter_init(depmod->symbols, &iter);
 
        while (hash_iter_next(&iter, NULL, &v)) {
                int duplicate;
                const struct symbol *sym = v;
+               size_t len;
 
                if (sym->owner == NULL)
                        continue;
 
-               strcpy(alias + baselen, sym->name);
+               len = strlen(sym->name);
+
+               if (scratchbuf_alloc(&salias, baselen + len + 1) < 0) {
+                       ret = -ENOMEM;
+                       goto err_scratchbuf;
+               }
+               memcpy(scratchbuf_str(&salias) + baselen, sym->name, len + 1);
                duplicate = index_insert(idx, alias, sym->owner->modname,
                                                        sym->owner->idx);
 
@@ -2116,9 +2102,14 @@ static int output_symbols_bin(struct depmod *depmod, FILE *out)
        }
 
        index_write(idx, out);
+
+err_scratchbuf:
        index_destroy(idx);
 
-       return 0;
+       if (ret < 0)
+               ERR("output symbols: %s\n", strerror(-ret));
+
+       return ret;
 }
 
 static int output_builtin_bin(struct depmod *depmod, FILE *out)
@@ -2164,8 +2155,7 @@ static int output_builtin_bin(struct depmod *depmod, FILE *out)
 static int output_devname(struct depmod *depmod, FILE *out)
 {
        size_t i;
-
-       fputs("# Device nodes to trigger on-demand module loading.\n", out);
+       bool empty = true;
 
        for (i = 0; i < depmod->modules.count; i++) {
                const struct mod *mod = depmod->modules.array[i];
@@ -2196,11 +2186,23 @@ static int output_devname(struct depmod *depmod, FILE *out)
                                minor = min;
                        }
 
-                       if (type != '\0' && devname != NULL) {
+                       if (type != '\0' && devname != NULL)
+                               break;
+               }
+
+               if (devname != NULL) {
+                       if (type != '\0') {
+                               if (empty) {
+                                       fputs("# Device nodes to trigger on-demand module loading.\n",
+                                             out);
+                                       empty = false;
+                               }
                                fprintf(out, "%s %s %c%u:%u\n", mod->modname,
                                        devname, type, major, minor);
-                               break;
-                       }
+                        } else
+                               ERR("Module '%s' has devname (%s) but "
+                                   "lacks major and minor information. "
+                                   "Ignoring.\n", mod->modname, devname);
                }
        }
 
@@ -2304,9 +2306,11 @@ static int depmod_output(struct depmod *depmod, FILE *out)
 static void depmod_add_fake_syms(struct depmod *depmod)
 {
        /* __this_module is magic inserted by kernel loader. */
-       depmod_symbol_add(depmod, "__this_module", 0, NULL);
+       depmod_symbol_add(depmod, "__this_module", true, 0, NULL);
        /* On S390, this is faked up too */
-       depmod_symbol_add(depmod, "_GLOBAL_OFFSET_TABLE_", 0, NULL);
+       depmod_symbol_add(depmod, "_GLOBAL_OFFSET_TABLE_", true, 0, NULL);
+       /* On PowerPC64 ABIv2, .TOC. is more or less _GLOBAL_OFFSET_TABLE_ */
+       depmod_symbol_add(depmod, "TOC.", true, 0, NULL);
 }
 
 static int depmod_load_symvers(struct depmod *depmod, const char *filename)
@@ -2347,7 +2351,7 @@ static int depmod_load_symvers(struct depmod *depmod, const char *filename)
                        continue;
                }
 
-               depmod_symbol_add(depmod, sym, crc, NULL);
+               depmod_symbol_add(depmod, sym, false, crc, NULL);
        }
        depmod_add_fake_syms(depmod);
 
@@ -2388,6 +2392,10 @@ static int depmod_load_system_map(struct depmod *depmod, const char *filename)
                        goto invalid_syntax;
                p++;
 
+               /* skip prefix */
+               if (p[0] == depmod->cfg->sym_prefix)
+                       p++;
+
                /* Covers gpl-only and normal symbols. */
                if (strncmp(p, ksymstr, ksymstr_len) != 0)
                        continue;
@@ -2396,7 +2404,7 @@ static int depmod_load_system_map(struct depmod *depmod, const char *filename)
                if (end != NULL)
                        *end = '\0';
 
-               depmod_symbol_add(depmod, p + ksymstr_len, 0, NULL);
+               depmod_symbol_add(depmod, p + ksymstr_len, true, 0, NULL);
                continue;
 
        invalid_syntax:
@@ -2429,7 +2437,7 @@ static int depfile_up_to_date_dir(DIR *d, time_t mtime, size_t baselen, char *pa
                namelen = strlen(name);
                if (baselen + namelen + 2 >= PATH_MAX) {
                        path[baselen] = '\0';
-                       ERR("path is too long %s%s %zd\n", path, name);
+                       ERR("path is too long %s%s\n", path, name);
                        continue;
                }
 
@@ -2465,18 +2473,9 @@ static int depfile_up_to_date_dir(DIR *d, time_t mtime, size_t baselen, char *pa
                                                     path);
                        closedir(subdir);
                } else if (S_ISREG(st.st_mode)) {
-                       const struct kmod_ext *eitr;
-                       uint8_t matches = 0;
-                       for (eitr = kmod_exts; eitr->ext != NULL; eitr++) {
-                               if (namelen <= eitr->len)
-                                       continue;
-                               if (streq(name + namelen - eitr->len, eitr->ext)) {
-                                       matches = 1;
-                                       break;
-                               }
-                       }
-                       if (!matches)
+                       if (!path_ends_with_kmod_ext(name, namelen))
                                continue;
+
                        memcpy(path + baselen, name, namelen + 1);
                        err = st.st_mtime <= mtime;
                        if (err == 0) {
@@ -2544,8 +2543,8 @@ static int do_depmod(int argc, char *argv[])
 {
        FILE *out = NULL;
        int err = 0, all = 0, maybe_all = 0, n_config_paths = 0;
-       char *root = NULL;
-       const char **config_paths = NULL;
+       _cleanup_free_ char *root = NULL;
+       _cleanup_free_ const char **config_paths = NULL;
        const char *system_map = NULL;
        const char *module_symvers = NULL;
        const char *null_kmod_config = NULL;
@@ -2570,6 +2569,8 @@ static int do_depmod(int argc, char *argv[])
                        maybe_all = 1;
                        break;
                case 'b':
+                       if (root)
+                               free(root);
                        root = path_make_absolute_cwd(optarg);
                        break;
                case 'C': {
@@ -2623,12 +2624,11 @@ static int do_depmod(int argc, char *argv[])
 
                        break;
                case 'h':
-                       help(basename(argv[0]));
-                       free(config_paths);
+                       help();
                        return EXIT_SUCCESS;
                case 'V':
                        puts(PACKAGE " version " VERSION);
-                       free(config_paths);
+                       puts(KMOD_FEATURES);
                        return EXIT_SUCCESS;
                case '?':
                        goto cmdline_failed;
@@ -2638,7 +2638,11 @@ static int do_depmod(int argc, char *argv[])
                }
        }
 
-       if (optind < argc && is_version_number(argv[optind])) {
+       if (optind < argc) {
+               if (!is_version_number(argv[optind])) {
+                       ERR("Bad version passed %s\n", argv[optind]);
+                       goto cmdline_failed;
+               }
                cfg.kversion = argv[optind];
                optind++;
        } else {
@@ -2660,11 +2664,8 @@ static int do_depmod(int argc, char *argv[])
                if (out == stdout)
                        goto done;
                /* ignore up-to-date errors (< 0) */
-               if (depfile_up_to_date(cfg.dirname) == 1) {
-                       DBG("%s/modules.dep is up to date!\n", cfg.dirname);
+               if (depfile_up_to_date(cfg.dirname) == 1)
                        goto done;
-               }
-               DBG("%s/modules.dep is outdated, do -a\n", cfg.dirname);
                all = 1;
        }
 
@@ -2673,7 +2674,8 @@ static int do_depmod(int argc, char *argv[])
                CRIT("kmod_new(\"%s\", {NULL}) failed: %m\n", cfg.dirname);
                goto cmdline_failed;
        }
-       kmod_set_log_priority(ctx, verbose);
+
+       log_setup_kmod_log(ctx, verbose);
 
        err = depmod_init(&depmod, &cfg, ctx);
        if (err < 0) {
@@ -2758,7 +2760,6 @@ static int do_depmod(int argc, char *argv[])
 done:
        depmod_shutdown(&depmod);
        cfg_free(&cfg);
-       free(config_paths);
        return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 
 cmdline_modules_failed:
@@ -2768,13 +2769,9 @@ depmod_init_failed:
                kmod_unref(ctx);
 cmdline_failed:
        cfg_free(&cfg);
-       free(config_paths);
-       free(root);
        return EXIT_FAILURE;
 }
 
-#include "kmod.h"
-
 const struct kmod_cmd kmod_cmd_compat_depmod = {
        .name = "depmod",
        .cmd = do_depmod,