]> git.ipfire.org Git - thirdparty/kmod.git/blobdiff - libkmod/libkmod-module.c
testsuite: fix test_array_sort pointers inderection
[thirdparty/kmod.git] / libkmod / libkmod-module.c
index 3d3325f2b1ee6e047fc78836f2e0b8441900cf03..bf6a8d60f7c4ea48a724d7d48636fcfe70e51106 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
 #include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fnmatch.h>
 #include <inttypes.h>
 #include <limits.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
+#include <sys/types.h>
 #include <sys/wait.h>
-#include <fnmatch.h>
-
 #ifdef HAVE_LINUX_MODULE_H
 #include <linux/module.h>
 #endif
 
+#include <shared/util.h>
+
 #include "libkmod.h"
 #include "libkmod-internal.h"
 
  * @short_description: operate on kernel modules
  */
 
+enum kmod_module_builtin {
+    KMOD_MODULE_BUILTIN_UNKNOWN,
+    KMOD_MODULE_BUILTIN_NO,
+    KMOD_MODULE_BUILTIN_YES,
+};
+
 /**
  * kmod_module:
  *
@@ -74,6 +80,13 @@ struct kmod_module {
                bool remove_commands : 1;
        } init;
 
+       /*
+        * mark if module is builtin, i.e. it's present on modules.builtin
+        * file. This is set as soon as it is needed or as soon as we know
+        * about it, i.e. the module was created from builtin lookup.
+        */
+       enum kmod_module_builtin builtin;
+
        /*
         * private field used by kmod_module_get_probe_list() to detect
         * dependency loops
@@ -87,11 +100,11 @@ struct kmod_module {
        bool ignorecmd : 1;
 
        /*
-        * if module was created by searching the modules.builtin file, this
-        * is set. There's nothing much useful one can do with such a
-        * "module", except knowing it's builtin.
+        * set by kmod_module_get_probe_list: indicates whether this is the
+        * module the user asked for or its dependency, or whether this
+        * is a softdep only
         */
-       bool builtin : 1;
+       bool required : 1;
 };
 
 static inline const char *path_join(const char *path, size_t prefixlen,
@@ -163,7 +176,7 @@ int kmod_module_parse_depline(struct kmod_module *mod, char *line)
        p++;
        for (p = strtok_r(p, " \t", &saveptr); p != NULL;
                                        p = strtok_r(NULL, " \t", &saveptr)) {
-               struct kmod_module *depmod;
+               struct kmod_module *depmod = NULL;
                const char *path;
 
                path = path_join(p, dirnamelen, buf);
@@ -205,9 +218,24 @@ void kmod_module_set_visited(struct kmod_module *mod, bool visited)
 
 void kmod_module_set_builtin(struct kmod_module *mod, bool builtin)
 {
-       mod->builtin = builtin;
+       mod->builtin =
+               builtin ? KMOD_MODULE_BUILTIN_YES : KMOD_MODULE_BUILTIN_NO;
+}
+
+void kmod_module_set_required(struct kmod_module *mod, bool required)
+{
+       mod->required = required;
 }
 
+bool kmod_module_is_builtin(struct kmod_module *mod)
+{
+       if (mod->builtin == KMOD_MODULE_BUILTIN_UNKNOWN) {
+               kmod_module_set_builtin(mod,
+                                       kmod_lookup_alias_is_builtin(mod->ctx, mod->name));
+       }
+
+       return mod->builtin == KMOD_MODULE_BUILTIN_YES;
+}
 /*
  * Memory layout with alias:
  *
@@ -408,8 +436,10 @@ KMOD_EXPORT int kmod_module_new_from_path(struct kmod_ctx *ctx,
        }
 
        err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m);
-       if (err < 0)
+       if (err < 0) {
+               free(abspath);
                return err;
+       }
 
        m->path = abspath;
        *mod = m;
@@ -790,8 +820,7 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
        int err;
        const void *mem;
        off_t size;
-       struct kmod_file *file;
-       struct kmod_elf *elf = NULL;
+       struct kmod_elf *elf;
        const char *path;
        const char *args = options ? options : "";
 
@@ -801,16 +830,16 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
        path = kmod_module_get_path(mod);
        if (path == NULL) {
                ERR(mod->ctx, "could not find module by name='%s'\n", mod->name);
-               return -ENOSYS;
+               return -ENOENT;
        }
 
-       file = kmod_file_open(mod->ctx, path);
-       if (file == NULL) {
+       mod->file = kmod_file_open(mod->ctx, path);
+       if (mod->file == NULL) {
                err = -errno;
                return err;
        }
 
-       if (kmod_file_get_direct(file)) {
+       if (kmod_file_get_direct(mod->file)) {
                unsigned int kernel_flags = 0;
 
                if (flags & KMOD_INSERT_FORCE_VERMAGIC)
@@ -818,19 +847,16 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
                if (flags & KMOD_INSERT_FORCE_MODVERSION)
                        kernel_flags |= MODULE_INIT_IGNORE_MODVERSIONS;
 
-               err = finit_module(kmod_file_get_fd(file), args, kernel_flags);
+               err = finit_module(kmod_file_get_fd(mod->file), args, kernel_flags);
                if (err == 0 || errno != ENOSYS)
                        goto init_finished;
        }
 
-       size = kmod_file_get_size(file);
-       mem = kmod_file_get_contents(file);
-
        if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) {
-               elf = kmod_elf_new(mem, size);
+               elf = kmod_file_get_elf(mod->file);
                if (elf == NULL) {
                        err = -errno;
-                       goto elf_failed;
+                       return err;
                }
 
                if (flags & KMOD_INSERT_FORCE_MODVERSION) {
@@ -846,7 +872,10 @@ KMOD_EXPORT int kmod_module_insert_module(struct kmod_module *mod,
                }
 
                mem = kmod_elf_get_memory(elf);
+       } else {
+               mem = kmod_file_get_contents(mod->file);
        }
+       size = kmod_file_get_size(mod->file);
 
        err = init_module(mem, size, args);
 init_finished:
@@ -854,12 +883,6 @@ init_finished:
                err = -errno;
                INFO(mod->ctx, "Failed to insert module '%s': %m\n", path);
        }
-
-       if (elf != NULL)
-               kmod_elf_unref(elf);
-elf_failed:
-       kmod_file_unref(file);
-
        return err;
 }
 
@@ -917,7 +940,8 @@ KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx,
                                module_is_blacklisted(mod))
                        continue;
 
-               if ((filter_type & KMOD_FILTER_BUILTIN) && mod->builtin)
+               if ((filter_type & KMOD_FILTER_BUILTIN)
+                   && kmod_module_is_builtin(mod))
                        continue;
 
                node = kmod_list_append(*output, mod);
@@ -1040,6 +1064,7 @@ static char *module_options_concat(const char *opt, const char *xopt)
 }
 
 static int __kmod_module_get_probe_list(struct kmod_module *mod,
+                                               bool required,
                                                bool ignorecmd,
                                                struct kmod_list **list);
 
@@ -1059,7 +1084,7 @@ static int __kmod_module_fill_softdep(struct kmod_module *mod,
 
        kmod_list_foreach(l, pre) {
                struct kmod_module *m = l->data;
-               err = __kmod_module_get_probe_list(m, false, list);
+               err = __kmod_module_get_probe_list(m, false, false, list);
                if (err < 0)
                        goto fail;
        }
@@ -1075,7 +1100,7 @@ static int __kmod_module_fill_softdep(struct kmod_module *mod,
 
        kmod_list_foreach(l, post) {
                struct kmod_module *m = l->data;
-               err = __kmod_module_get_probe_list(m, false, list);
+               err = __kmod_module_get_probe_list(m, false, false, list);
                if (err < 0)
                        goto fail;
        }
@@ -1089,6 +1114,7 @@ fail:
 
 /* re-entrant */
 static int __kmod_module_get_probe_list(struct kmod_module *mod,
+                                               bool required,
                                                bool ignorecmd,
                                                struct kmod_list **list)
 {
@@ -1103,6 +1129,19 @@ static int __kmod_module_get_probe_list(struct kmod_module *mod,
        mod->visited = true;
 
        dep = kmod_module_get_dependencies(mod);
+       if (required) {
+               /*
+                * Called from kmod_module_probe_insert_module(); set the
+                * ->required flag on mod and all its dependencies before
+                * they are possibly visited through some softdeps.
+                */
+               mod->required = true;
+               kmod_list_foreach(l, dep) {
+                       struct kmod_module *m = l->data;
+                       m->required = true;
+               }
+       }
+
        kmod_list_foreach(l, dep) {
                struct kmod_module *m = l->data;
                err = __kmod_module_fill_softdep(m, list);
@@ -1140,8 +1179,9 @@ static int kmod_module_get_probe_list(struct kmod_module *mod,
         * Make sure we don't get screwed by previous calls to this function
         */
        kmod_set_modules_visited(mod->ctx, false);
+       kmod_set_modules_required(mod->ctx, false);
 
-       err = __kmod_module_get_probe_list(mod, ignorecmd, list);
+       err = __kmod_module_get_probe_list(mod, true, ignorecmd, list);
        if (err < 0) {
                kmod_module_unref_list(*list);
                *list = NULL;
@@ -1302,8 +1342,12 @@ finish_module:
                                (flags & KMOD_PROBE_FAIL_ON_LOADED))
                        break;
 
-               if (err == -EEXIST)
+               /*
+                * Ignore errors from softdeps
+                */
+               if (err == -EEXIST || !m->required)
                        err = 0;
+
                else if (err < 0)
                        break;
        }
@@ -1616,13 +1660,14 @@ KMOD_EXPORT int kmod_module_new_from_loaded(struct kmod_ctx *ctx,
                struct kmod_module *m;
                struct kmod_list *node;
                int err;
+               size_t len = strlen(line);
                char *saveptr, *name = strtok_r(line, " \t", &saveptr);
 
                err = kmod_module_new_from_name(ctx, name, &m);
                if (err < 0) {
                        ERR(ctx, "could not get module from name '%s': %s\n",
                                name, strerror(-err));
-                       continue;
+                       goto eat_line;
                }
 
                node = kmod_list_append(l, m);
@@ -1632,6 +1677,9 @@ KMOD_EXPORT int kmod_module_new_from_loaded(struct kmod_ctx *ctx,
                        ERR(ctx, "out of memory\n");
                        kmod_module_unref(m);
                }
+eat_line:
+               while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+                       len = strlen(line);
        }
 
        fclose(fp);
@@ -1686,7 +1734,8 @@ KMOD_EXPORT int kmod_module_get_initstate(const struct kmod_module *mod)
        if (mod == NULL)
                return -ENOENT;
 
-       if (mod->builtin)
+       /* remove const: this can only change internal state */
+       if (kmod_module_is_builtin((struct kmod_module *)mod))
                return KMOD_MODULE_BUILTIN;
 
        pathlen = snprintf(path, sizeof(path),
@@ -1702,7 +1751,7 @@ KMOD_EXPORT int kmod_module_get_initstate(const struct kmod_module *mod)
                        struct stat st;
                        path[pathlen - (sizeof("/initstate") - 1)] = '\0';
                        if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
-                               return KMOD_MODULE_BUILTIN;
+                               return KMOD_MODULE_COMING;
                }
 
                DBG(mod->ctx, "could not open '%s': %s\n",
@@ -1756,7 +1805,7 @@ KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
         * loaded.
         */
        snprintf(line, sizeof(line), "/sys/module/%s", mod->name);
-       dfd = open(line, O_RDONLY);
+       dfd = open(line, O_RDONLY|O_CLOEXEC);
        if (dfd < 0)
                return -errno;
 
@@ -1775,16 +1824,18 @@ KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
                int err = -errno;
                ERR(mod->ctx,
                    "could not open /proc/modules: %s\n", strerror(errno));
+               close(dfd);
                return err;
        }
 
        while (fgets(line, sizeof(line), fp)) {
+               size_t len = strlen(line);
                char *saveptr, *endptr, *tok = strtok_r(line, " \t", &saveptr);
                long value;
 
                lineno++;
                if (tok == NULL || !streq(tok, mod->name))
-                       continue;
+                       goto eat_line;
 
                tok = strtok_r(NULL, " \t", &saveptr);
                if (tok == NULL) {
@@ -1802,6 +1853,9 @@ KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
 
                size = value;
                break;
+eat_line:
+               while (line[len - 1] != '\n' && fgets(line, sizeof(line), fp))
+                       len = strlen(line);
        }
        fclose(fp);
 
@@ -1817,7 +1871,7 @@ done:
  * Get the ref count of this @mod, as returned by Linux Kernel, by reading
  * /sys filesystem.
  *
- * Returns: 0 on success or < 0 on failure.
+ * Returns: the reference count on success or < 0 on failure.
  */
 KMOD_EXPORT int kmod_module_get_refcnt(const struct kmod_module *mod)
 {
@@ -2201,29 +2255,43 @@ KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_
                struct kmod_list *n;
                char *key_hex;
 
-               n = kmod_module_info_append(list, "signer", strlen("signer"),
-                               sig_info.signer, sig_info.signer_len);
+               n = kmod_module_info_append(list, "signature", strlen("sig_id"),
+                               sig_info.id_type, strlen(sig_info.id_type));
                if (n == NULL)
                        goto list_error;
                count++;
 
-               /* Display the key id as 01:12:DE:AD:BE:EF:... */
-               key_hex = malloc(sig_info.key_id_len * 3);
-               if (key_hex == NULL)
-                       goto list_error;
-               for (i = 0; i < (int)sig_info.key_id_len; i++) {
-                       sprintf(key_hex + i * 3, "%02X",
-                                       (unsigned char)sig_info.key_id[i]);
-                       if (i < (int)sig_info.key_id_len - 1)
-                               key_hex[i * 3 + 2] = ':';
-               }
-               n = kmod_module_info_append(list, "sig_key", strlen("sig_key"),
-                               key_hex, sig_info.key_id_len * 3 - 1);
-               free(key_hex);
+               n = kmod_module_info_append(list, "signer", strlen("signer"),
+                               sig_info.signer, sig_info.signer_len);
                if (n == NULL)
                        goto list_error;
                count++;
 
+               if (sig_info.key_id_len) {
+                       /* Display the key id as 01:12:DE:AD:BE:EF:... */
+                       key_hex = malloc(sig_info.key_id_len * 3);
+                       if (key_hex == NULL)
+                               goto list_error;
+                       for (i = 0; i < (int)sig_info.key_id_len; i++) {
+                               sprintf(key_hex + i * 3, "%02X",
+                                               (unsigned char)sig_info.key_id[i]);
+                               if (i < (int)sig_info.key_id_len - 1)
+                                       key_hex[i * 3 + 2] = ':';
+                       }
+                       n = kmod_module_info_append(list, "sig_key", strlen("sig_key"),
+                                       key_hex, sig_info.key_id_len * 3 - 1);
+                       free(key_hex);
+                       if (n == NULL)
+                               goto list_error;
+                       count++;
+               } else {
+                       n = kmod_module_info_append(list, "sig_key", strlen("sig_key"),
+                                       NULL, 0);
+                       if (n == NULL)
+                               goto list_error;
+                       count++;
+               }
+
                n = kmod_module_info_append(list,
                                "sig_hashalgo", strlen("sig_hashalgo"),
                                sig_info.hash_algo, strlen(sig_info.hash_algo));
@@ -2232,7 +2300,7 @@ KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_
                count++;
 
                /*
-                * Omit sig_info.id_type and sig_info.algo for now, as these
+                * Omit sig_info.algo for now, as these
                 * are currently constant.
                 */
        }