summaryrefslogtreecommitdiffstats
path: root/libkmod/libkmod-builtin.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:03:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:03:56 +0000
commit18da3ffcd7f3c8a0c5f790c801b5813503c2273d (patch)
tree84caf98dc5cef3d123c56ba12e35fd67026e0693 /libkmod/libkmod-builtin.c
parentInitial commit. (diff)
downloadkmod-18da3ffcd7f3c8a0c5f790c801b5813503c2273d.tar.xz
kmod-18da3ffcd7f3c8a0c5f790c801b5813503c2273d.zip
Adding upstream version 31+20240202.upstream/31+20240202
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libkmod/libkmod-builtin.c')
-rw-r--r--libkmod/libkmod-builtin.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/libkmod/libkmod-builtin.c b/libkmod/libkmod-builtin.c
new file mode 100644
index 0000000..65334a8
--- /dev/null
+++ b/libkmod/libkmod-builtin.c
@@ -0,0 +1,330 @@
+/*
+ * libkmod - interface to kernel built-in modules
+ *
+ * Copyright (C) 2019 Alexey Gladkov <gladkov.alexey@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libkmod.h"
+#include "libkmod-internal.h"
+
+#define MODULES_BUILTIN_MODINFO "modules.builtin.modinfo"
+
+struct kmod_builtin_iter {
+ struct kmod_ctx *ctx;
+
+ // The file descriptor.
+ int file;
+
+ // The total size in bytes.
+ ssize_t size;
+
+ // The offset of current module.
+ off_t pos;
+
+ // The offset at which the next module is located.
+ off_t next;
+
+ // Number of strings in the current block.
+ ssize_t nstrings;
+
+ // Internal buffer and its size.
+ size_t bufsz;
+ char *buf;
+};
+
+static struct kmod_builtin_iter *kmod_builtin_iter_new(struct kmod_ctx *ctx)
+{
+ char path[PATH_MAX];
+ int file, sv_errno;
+ struct stat sb;
+ struct kmod_builtin_iter *iter = NULL;
+ const char *dirname = kmod_get_dirname(ctx);
+ size_t len = strlen(dirname);
+
+ file = -1;
+
+ if ((len + 1 + strlen(MODULES_BUILTIN_MODINFO) + 1) >= PATH_MAX) {
+ sv_errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ snprintf(path, PATH_MAX, "%s/%s", dirname, MODULES_BUILTIN_MODINFO);
+
+ file = open(path, O_RDONLY|O_CLOEXEC);
+ if (file < 0) {
+ sv_errno = errno;
+ goto fail;
+ }
+
+ if (fstat(file, &sb) < 0) {
+ sv_errno = errno;
+ goto fail;
+ }
+
+ iter = malloc(sizeof(*iter));
+ if (!iter) {
+ sv_errno = ENOMEM;
+ goto fail;
+ }
+
+ iter->ctx = ctx;
+ iter->file = file;
+ iter->size = sb.st_size;
+ iter->nstrings = 0;
+ iter->pos = 0;
+ iter->next = 0;
+ iter->bufsz = 0;
+ iter->buf = NULL;
+
+ return iter;
+fail:
+ if (file >= 0)
+ close(file);
+
+ errno = sv_errno;
+
+ return iter;
+}
+
+static void kmod_builtin_iter_free(struct kmod_builtin_iter *iter)
+{
+ close(iter->file);
+ free(iter->buf);
+ free(iter);
+}
+
+static off_t get_string(struct kmod_builtin_iter *iter, off_t offset,
+ char **line, size_t *size)
+{
+ int sv_errno;
+ char *nullp = NULL;
+ size_t linesz = 0;
+
+ while (!nullp) {
+ char buf[BUFSIZ];
+ ssize_t sz;
+ size_t partsz;
+
+ sz = pread(iter->file, buf, BUFSIZ, offset);
+ if (sz < 0) {
+ sv_errno = errno;
+ goto fail;
+ } else if (sz == 0) {
+ offset = 0;
+ break;
+ }
+
+ nullp = memchr(buf, '\0', (size_t) sz);
+ partsz = (size_t)((nullp) ? (nullp - buf) + 1 : sz);
+ offset += (off_t) partsz;
+
+ if (iter->bufsz < linesz + partsz) {
+ iter->bufsz = linesz + partsz;
+ iter->buf = realloc(iter->buf, iter->bufsz);
+
+ if (!iter->buf) {
+ sv_errno = errno;
+ goto fail;
+ }
+ }
+
+ strncpy(iter->buf + linesz, buf, partsz);
+ linesz += partsz;
+ }
+
+ if (linesz) {
+ *line = iter->buf;
+ *size = linesz;
+ }
+
+ return offset;
+fail:
+ errno = sv_errno;
+ return -1;
+}
+
+static bool kmod_builtin_iter_next(struct kmod_builtin_iter *iter)
+{
+ char *line, *modname;
+ size_t linesz;
+ off_t pos, offset, modlen;
+
+ modname = NULL;
+
+ iter->nstrings = 0;
+ offset = pos = iter->next;
+
+ while (offset < iter->size) {
+ char *dot;
+ off_t len;
+
+ offset = get_string(iter, pos, &line, &linesz);
+ if (offset <= 0) {
+ if (offset)
+ ERR(iter->ctx, "get_string: %s\n", strerror(errno));
+ pos = iter->size;
+ break;
+ }
+
+ dot = strchr(line, '.');
+ if (!dot) {
+ ERR(iter->ctx, "kmod_builtin_iter_next: unexpected string without modname prefix\n");
+ pos = iter->size;
+ break;
+ }
+
+ len = dot - line;
+
+ if (!modname) {
+ modname = strdup(line);
+ modlen = len;
+ } else if (modlen != len || strncmp(modname, line, len)) {
+ break;
+ }
+
+ iter->nstrings++;
+ pos = offset;
+ }
+
+ iter->pos = iter->next;
+ iter->next = pos;
+
+ free(modname);
+
+ return (iter->pos < iter->size);
+}
+
+static bool kmod_builtin_iter_get_modname(struct kmod_builtin_iter *iter,
+ char modname[static PATH_MAX])
+{
+ int sv_errno;
+ char *line, *dot;
+ size_t linesz, len;
+ off_t offset;
+
+ if (iter->pos == iter->size)
+ return false;
+
+ line = NULL;
+
+ offset = get_string(iter, iter->pos, &line, &linesz);
+ if (offset <= 0) {
+ sv_errno = errno;
+ if (offset)
+ ERR(iter->ctx, "get_string: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ dot = strchr(line, '.');
+ if (!dot) {
+ sv_errno = errno;
+ ERR(iter->ctx, "kmod_builtin_iter_get_modname: unexpected string without modname prefix\n");
+ goto fail;
+ }
+
+ len = dot - line;
+
+ if (len >= PATH_MAX) {
+ sv_errno = ENAMETOOLONG;
+ goto fail;
+ }
+
+ strncpy(modname, line, len);
+ modname[len] = '\0';
+
+ return true;
+fail:
+ errno = sv_errno;
+ return false;
+}
+
+/* array will be allocated with strings in a single malloc, just free *array */
+ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname,
+ char ***modinfo)
+{
+ ssize_t count = 0;
+ char *s, *line = NULL;
+ size_t i, n, linesz, modlen, size;
+ off_t pos, offset;
+
+ char *name = NULL;
+ char buf[PATH_MAX];
+
+ struct kmod_builtin_iter *iter = kmod_builtin_iter_new(ctx);
+
+ if (!iter)
+ return -errno;
+
+ while (!name && kmod_builtin_iter_next(iter)) {
+ if (!kmod_builtin_iter_get_modname(iter, buf)) {
+ count = -errno;
+ goto fail;
+ }
+
+ if (strcmp(modname, buf))
+ continue;
+
+ name = buf;
+ }
+
+ if (!name) {
+ count = -ENOSYS;
+ goto fail;
+ }
+
+ modlen = strlen(modname) + 1;
+ count = iter->nstrings;
+ size = iter->next - iter->pos - (modlen * count);
+
+ *modinfo = malloc(size + sizeof(char *) * (count + 1));
+ if (!*modinfo) {
+ count = -errno;
+ goto fail;
+ }
+
+ s = (char *)(*modinfo + count + 1);
+ i = 0;
+
+ n = 0;
+ offset = pos = iter->pos;
+
+ while (offset < iter->next) {
+ offset = get_string(iter, pos, &line, &linesz);
+ if (offset <= 0) {
+ count = (offset) ? -errno : -EINVAL;
+ free(*modinfo);
+ goto fail;
+ }
+
+ strcpy(s + i, line + modlen);
+ (*modinfo)[n++] = s + i;
+ i += linesz - modlen;
+
+ pos = offset;
+ }
+fail:
+ kmod_builtin_iter_free(iter);
+ return count;
+}