2011-12-17 01:08:53 +07:00
|
|
|
/*
|
|
|
|
* libkmod - interface to kernel module operations
|
|
|
|
*
|
2013-01-16 20:27:21 +07:00
|
|
|
* Copyright (C) 2011-2013 ProFUSION embedded systems
|
2011-12-17 01:08:53 +07:00
|
|
|
*
|
|
|
|
* 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
|
2014-12-26 08:32:03 +07:00
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2011-12-17 01:08:53 +07:00
|
|
|
*/
|
|
|
|
|
2014-10-03 11:41:42 +07:00
|
|
|
#include <errno.h>
|
2011-12-24 20:58:30 +07:00
|
|
|
#include <stdbool.h>
|
2011-12-17 01:08:53 +07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/mman.h>
|
2014-10-03 11:41:42 +07:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2011-12-17 01:08:53 +07:00
|
|
|
#include <unistd.h>
|
2020-09-09 02:59:20 +07:00
|
|
|
#ifdef ENABLE_ZSTD
|
|
|
|
#include <zstd.h>
|
|
|
|
#endif
|
2011-12-24 20:58:30 +07:00
|
|
|
#ifdef ENABLE_XZ
|
|
|
|
#include <lzma.h>
|
|
|
|
#endif
|
2011-12-17 01:08:53 +07:00
|
|
|
#ifdef ENABLE_ZLIB
|
|
|
|
#include <zlib.h>
|
|
|
|
#endif
|
|
|
|
|
2014-10-03 11:41:42 +07:00
|
|
|
#include <shared/util.h>
|
|
|
|
|
|
|
|
#include "libkmod.h"
|
|
|
|
#include "libkmod-internal.h"
|
|
|
|
|
2011-12-17 01:08:53 +07:00
|
|
|
struct kmod_file {
|
2011-12-24 09:09:31 +07:00
|
|
|
int fd;
|
2023-06-02 05:39:59 +07:00
|
|
|
enum kmod_file_compression_type compression;
|
2011-12-17 01:08:53 +07:00
|
|
|
off_t size;
|
|
|
|
void *memory;
|
2024-02-13 00:23:06 +07:00
|
|
|
int (*load)(struct kmod_file *file);
|
2012-01-04 17:19:34 +07:00
|
|
|
const struct kmod_ctx *ctx;
|
2012-10-18 11:36:33 +07:00
|
|
|
struct kmod_elf *elf;
|
2011-12-17 01:08:53 +07:00
|
|
|
};
|
|
|
|
|
2020-09-09 02:59:20 +07:00
|
|
|
#ifdef ENABLE_ZSTD
|
|
|
|
static int zstd_read_block(struct kmod_file *file, size_t block_size,
|
|
|
|
ZSTD_inBuffer *input, size_t *input_capacity)
|
|
|
|
{
|
|
|
|
ssize_t rdret;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (*input_capacity < block_size) {
|
|
|
|
free((void *)input->src);
|
|
|
|
input->src = malloc(block_size);
|
|
|
|
if (input->src == NULL) {
|
|
|
|
ret = -errno;
|
|
|
|
ERR(file->ctx, "zstd: %m\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
*input_capacity = block_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
rdret = read(file->fd, (void *)input->src, block_size);
|
|
|
|
if (rdret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
ERR(file->ctx, "zstd: %m\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
input->pos = 0;
|
|
|
|
input->size = rdret;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free)
|
|
|
|
{
|
|
|
|
uint8_t *old_buffer = buffer->dst;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (buffer->size - buffer->pos >= min_free)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
buffer->size += min_free;
|
|
|
|
buffer->dst = realloc(buffer->dst, buffer->size);
|
|
|
|
if (buffer->dst == NULL) {
|
|
|
|
ret = -errno;
|
|
|
|
free(old_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr,
|
|
|
|
ZSTD_inBuffer *input, ZSTD_outBuffer *output,
|
|
|
|
size_t *next_block_size)
|
|
|
|
{
|
|
|
|
size_t out_buf_min_size = ZSTD_DStreamOutSize();
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ssize_t dsret;
|
|
|
|
|
|
|
|
ret = zstd_ensure_outbuffer_space(output, out_buf_min_size);
|
|
|
|
if (ret) {
|
|
|
|
ERR(file->ctx, "zstd: %s\n", strerror(-ret));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsret = ZSTD_decompressStream(dstr, output, input);
|
|
|
|
if (ZSTD_isError(dsret)) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dsret > 0)
|
|
|
|
*next_block_size = (size_t)dsret;
|
|
|
|
} while (input->pos < input->size
|
|
|
|
|| output->pos > output->size
|
|
|
|
|| output->size - output->pos < out_buf_min_size);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int load_zstd(struct kmod_file *file)
|
|
|
|
{
|
|
|
|
ZSTD_DStream *dstr;
|
|
|
|
size_t next_block_size;
|
|
|
|
size_t zst_inb_capacity = 0;
|
|
|
|
ZSTD_inBuffer zst_inb = { 0 };
|
|
|
|
ZSTD_outBuffer zst_outb = { 0 };
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dstr = ZSTD_createDStream();
|
|
|
|
if (dstr == NULL) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
ERR(file->ctx, "zstd: Failed to create decompression stream\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
next_block_size = ZSTD_initDStream(dstr);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
ret = zstd_read_block(file, next_block_size, &zst_inb,
|
|
|
|
&zst_inb_capacity);
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
|
|
|
if (zst_inb.size == 0) /* EOF */
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb,
|
|
|
|
&next_block_size);
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ZSTD_freeDStream(dstr);
|
|
|
|
free((void *)zst_inb.src);
|
|
|
|
file->memory = zst_outb.dst;
|
|
|
|
file->size = zst_outb.pos;
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
if (dstr != NULL)
|
|
|
|
ZSTD_freeDStream(dstr);
|
|
|
|
free((void *)zst_inb.src);
|
|
|
|
free((void *)zst_outb.dst);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
|
|
|
|
#endif
|
|
|
|
|
2011-12-24 20:58:30 +07:00
|
|
|
#ifdef ENABLE_XZ
|
2012-01-04 17:23:15 +07:00
|
|
|
static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
|
2011-12-24 20:58:30 +07:00
|
|
|
{
|
|
|
|
switch (ret) {
|
|
|
|
case LZMA_MEM_ERROR:
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
|
2011-12-24 20:58:30 +07:00
|
|
|
break;
|
|
|
|
case LZMA_FORMAT_ERROR:
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: File format not recognized\n");
|
2011-12-24 20:58:30 +07:00
|
|
|
break;
|
|
|
|
case LZMA_OPTIONS_ERROR:
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: Unsupported compression options\n");
|
2011-12-24 20:58:30 +07:00
|
|
|
break;
|
|
|
|
case LZMA_DATA_ERROR:
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: File is corrupt\n");
|
2011-12-24 20:58:30 +07:00
|
|
|
break;
|
|
|
|
case LZMA_BUF_ERROR:
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: Unexpected end of input\n");
|
2011-12-24 20:58:30 +07:00
|
|
|
break;
|
|
|
|
default:
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: Internal error (bug)\n");
|
2011-12-24 20:58:30 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
|
|
|
|
{
|
|
|
|
uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
|
|
|
|
lzma_action action = LZMA_RUN;
|
|
|
|
lzma_ret ret;
|
|
|
|
void *p = NULL;
|
|
|
|
size_t total = 0;
|
|
|
|
|
|
|
|
strm->avail_in = 0;
|
|
|
|
strm->next_out = out_buf;
|
|
|
|
strm->avail_out = sizeof(out_buf);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (strm->avail_in == 0) {
|
|
|
|
ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
|
|
|
|
if (rdret < 0) {
|
|
|
|
ret = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
strm->next_in = in_buf;
|
|
|
|
strm->avail_in = rdret;
|
|
|
|
if (rdret == 0)
|
|
|
|
action = LZMA_FINISH;
|
|
|
|
}
|
|
|
|
ret = lzma_code(strm, action);
|
|
|
|
if (strm->avail_out == 0 || ret != LZMA_OK) {
|
|
|
|
size_t write_size = BUFSIZ - strm->avail_out;
|
|
|
|
char *tmp = realloc(p, total + write_size);
|
|
|
|
if (tmp == NULL) {
|
|
|
|
ret = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
memcpy(tmp + total, out_buf, write_size);
|
|
|
|
total += write_size;
|
|
|
|
p = tmp;
|
|
|
|
strm->next_out = out_buf;
|
|
|
|
strm->avail_out = BUFSIZ;
|
|
|
|
}
|
|
|
|
if (ret == LZMA_STREAM_END)
|
|
|
|
break;
|
|
|
|
if (ret != LZMA_OK) {
|
2012-01-04 17:23:15 +07:00
|
|
|
xz_uncompress_belch(file, ret);
|
2011-12-24 20:58:30 +07:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
file->memory = p;
|
|
|
|
file->size = total;
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
free(p);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-03 23:25:49 +07:00
|
|
|
static int load_xz(struct kmod_file *file)
|
2011-12-24 20:58:30 +07:00
|
|
|
{
|
|
|
|
lzma_stream strm = LZMA_STREAM_INIT;
|
|
|
|
lzma_ret lzret;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
|
|
|
|
if (lzret == LZMA_MEM_ERROR) {
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
|
2011-12-24 20:58:30 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
} else if (lzret != LZMA_OK) {
|
2012-01-04 17:23:15 +07:00
|
|
|
ERR(file->ctx, "xz: Internal error (bug)\n");
|
2011-12-24 20:58:30 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
ret = xz_uncompress(&strm, file);
|
|
|
|
lzma_end(&strm);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-03 23:25:49 +07:00
|
|
|
static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ENABLE_ZLIB
|
|
|
|
#define READ_STEP (4 * 1024 * 1024)
|
|
|
|
static int load_zlib(struct kmod_file *file)
|
2011-12-17 01:08:53 +07:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
off_t did = 0, total = 0;
|
2013-11-18 20:43:10 +07:00
|
|
|
_cleanup_free_ unsigned char *p = NULL;
|
2024-02-13 00:23:03 +07:00
|
|
|
gzFile gzf;
|
2024-02-13 00:23:02 +07:00
|
|
|
int gzfd;
|
2011-12-17 01:08:53 +07:00
|
|
|
|
|
|
|
errno = 0;
|
2024-02-13 00:23:02 +07:00
|
|
|
gzfd = fcntl(file->fd, F_DUPFD_CLOEXEC, 3);
|
|
|
|
if (gzfd < 0)
|
|
|
|
return -errno;
|
|
|
|
|
2024-02-13 00:23:03 +07:00
|
|
|
gzf = gzdopen(gzfd, "rb"); /* takes ownership of the fd */
|
|
|
|
if (gzf == NULL) {
|
2024-02-13 00:23:02 +07:00
|
|
|
close(gzfd);
|
2011-12-17 01:08:53 +07:00
|
|
|
return -errno;
|
2024-02-13 00:23:02 +07:00
|
|
|
}
|
2011-12-17 01:08:53 +07:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (did == total) {
|
|
|
|
void *tmp = realloc(p, total + READ_STEP);
|
|
|
|
if (tmp == NULL) {
|
|
|
|
err = -errno;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
total += READ_STEP;
|
|
|
|
p = tmp;
|
|
|
|
}
|
|
|
|
|
2024-02-13 00:23:03 +07:00
|
|
|
r = gzread(gzf, p + did, total - did);
|
2011-12-17 01:08:53 +07:00
|
|
|
if (r == 0)
|
|
|
|
break;
|
|
|
|
else if (r < 0) {
|
2012-05-08 06:41:41 +07:00
|
|
|
int gzerr;
|
2024-02-13 00:23:03 +07:00
|
|
|
const char *gz_errmsg = gzerror(gzf, &gzerr);
|
2012-05-08 06:41:41 +07:00
|
|
|
|
|
|
|
ERR(file->ctx, "gzip: %s\n", gz_errmsg);
|
|
|
|
|
|
|
|
/* gzip might not set errno here */
|
|
|
|
err = gzerr == Z_ERRNO ? -errno : -EINVAL;
|
2011-12-17 01:08:53 +07:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
did += r;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->memory = p;
|
|
|
|
file->size = did;
|
2013-11-18 20:43:10 +07:00
|
|
|
p = NULL;
|
2024-02-13 00:23:03 +07:00
|
|
|
gzclose(gzf);
|
2011-12-17 01:08:53 +07:00
|
|
|
return 0;
|
2013-11-18 20:43:10 +07:00
|
|
|
|
2011-12-17 01:08:53 +07:00
|
|
|
error:
|
2024-02-13 00:23:03 +07:00
|
|
|
gzclose(gzf); /* closes the gzfd */
|
2011-12-17 01:08:53 +07:00
|
|
|
return err;
|
|
|
|
}
|
2012-01-03 23:25:49 +07:00
|
|
|
|
|
|
|
static const char magic_zlib[] = {0x1f, 0x8b};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const struct comp_type {
|
|
|
|
size_t magic_size;
|
2023-06-02 05:39:59 +07:00
|
|
|
enum kmod_file_compression_type compression;
|
2012-01-03 23:25:49 +07:00
|
|
|
const char *magic_bytes;
|
2024-02-13 00:23:06 +07:00
|
|
|
int (*load)(struct kmod_file *file);
|
2012-01-03 23:25:49 +07:00
|
|
|
} comp_types[] = {
|
2020-09-09 02:59:20 +07:00
|
|
|
#ifdef ENABLE_ZSTD
|
2024-02-13 00:23:06 +07:00
|
|
|
{sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, load_zstd},
|
2020-09-09 02:59:20 +07:00
|
|
|
#endif
|
2012-01-03 23:25:49 +07:00
|
|
|
#ifdef ENABLE_XZ
|
2024-02-13 00:23:06 +07:00
|
|
|
{sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, load_xz},
|
2012-01-03 23:25:49 +07:00
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_ZLIB
|
2024-02-13 00:23:06 +07:00
|
|
|
{sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, load_zlib},
|
2011-12-24 09:09:31 +07:00
|
|
|
#endif
|
2024-02-13 00:23:06 +07:00
|
|
|
{0, KMOD_FILE_COMPRESSION_NONE, NULL, NULL}
|
2012-01-03 23:25:49 +07:00
|
|
|
};
|
2011-12-24 09:09:31 +07:00
|
|
|
|
2012-01-03 23:25:49 +07:00
|
|
|
static int load_reg(struct kmod_file *file)
|
2011-12-17 01:08:53 +07:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
2012-01-03 23:25:49 +07:00
|
|
|
if (fstat(file->fd, &st) < 0)
|
|
|
|
return -errno;
|
2011-12-17 01:08:53 +07:00
|
|
|
|
|
|
|
file->size = st.st_size;
|
2013-02-19 03:02:34 +07:00
|
|
|
file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
|
|
|
|
file->fd, 0);
|
2024-02-13 00:23:05 +07:00
|
|
|
if (file->memory == MAP_FAILED) {
|
|
|
|
file->memory = NULL;
|
2012-01-03 23:25:49 +07:00
|
|
|
return -errno;
|
2024-02-13 00:23:05 +07:00
|
|
|
}
|
2023-06-02 05:39:59 +07:00
|
|
|
|
2011-12-17 01:08:53 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-18 11:36:33 +07:00
|
|
|
struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
|
|
|
|
{
|
|
|
|
if (file->elf)
|
|
|
|
return file->elf;
|
|
|
|
|
2023-06-02 05:39:57 +07:00
|
|
|
kmod_file_load_contents(file);
|
2012-10-18 11:36:33 +07:00
|
|
|
file->elf = kmod_elf_new(file->memory, file->size);
|
|
|
|
return file->elf;
|
|
|
|
}
|
|
|
|
|
2012-01-04 17:19:34 +07:00
|
|
|
struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
|
|
|
|
const char *filename)
|
2011-12-17 01:08:53 +07:00
|
|
|
{
|
|
|
|
struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
|
2012-01-03 23:25:49 +07:00
|
|
|
const struct comp_type *itr;
|
|
|
|
size_t magic_size_max = 0;
|
2023-06-02 05:39:57 +07:00
|
|
|
int err = 0;
|
2011-12-17 01:08:53 +07:00
|
|
|
|
|
|
|
if (file == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2011-12-24 09:09:31 +07:00
|
|
|
file->fd = open(filename, O_RDONLY|O_CLOEXEC);
|
|
|
|
if (file->fd < 0) {
|
|
|
|
err = -errno;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2024-02-13 00:23:06 +07:00
|
|
|
for (itr = comp_types; itr->load != NULL; itr++) {
|
2012-01-03 23:25:49 +07:00
|
|
|
if (magic_size_max < itr->magic_size)
|
|
|
|
magic_size_max = itr->magic_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (magic_size_max > 0) {
|
|
|
|
char *buf = alloca(magic_size_max + 1);
|
|
|
|
ssize_t sz;
|
2011-12-17 01:08:53 +07:00
|
|
|
|
2012-01-03 23:25:49 +07:00
|
|
|
if (buf == NULL) {
|
|
|
|
err = -errno;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
sz = read_str_safe(file->fd, buf, magic_size_max + 1);
|
|
|
|
lseek(file->fd, 0, SEEK_SET);
|
|
|
|
if (sz != (ssize_t)magic_size_max) {
|
|
|
|
if (sz < 0)
|
|
|
|
err = sz;
|
|
|
|
else
|
|
|
|
err = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2024-02-13 00:23:06 +07:00
|
|
|
for (itr = comp_types; itr->load != NULL; itr++) {
|
2023-06-02 05:39:59 +07:00
|
|
|
if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
|
2024-02-13 00:23:06 +07:00
|
|
|
file->load = itr->load;
|
2023-06-02 05:39:59 +07:00
|
|
|
file->compression = itr->compression;
|
2012-01-03 23:25:49 +07:00
|
|
|
break;
|
2023-06-02 05:39:59 +07:00
|
|
|
}
|
2012-01-03 23:25:49 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-13 00:23:06 +07:00
|
|
|
if (file->load == NULL) {
|
|
|
|
file->load = load_reg;
|
2023-06-02 05:39:59 +07:00
|
|
|
file->compression = KMOD_FILE_COMPRESSION_NONE;
|
|
|
|
}
|
2012-01-03 23:25:49 +07:00
|
|
|
|
2012-01-04 17:19:34 +07:00
|
|
|
file->ctx = ctx;
|
2023-06-02 05:39:57 +07:00
|
|
|
|
2011-12-24 09:09:31 +07:00
|
|
|
error:
|
2011-12-17 01:08:53 +07:00
|
|
|
if (err < 0) {
|
2012-01-03 23:25:49 +07:00
|
|
|
if (file->fd >= 0)
|
|
|
|
close(file->fd);
|
2011-12-17 01:08:53 +07:00
|
|
|
free(file);
|
|
|
|
errno = -err;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2023-06-02 05:39:57 +07:00
|
|
|
/*
|
|
|
|
* Callers should just check file->memory got updated
|
|
|
|
*/
|
|
|
|
void kmod_file_load_contents(struct kmod_file *file)
|
|
|
|
{
|
|
|
|
if (file->memory)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* The load functions already log possible errors. */
|
2024-02-13 00:23:06 +07:00
|
|
|
file->load(file);
|
2023-06-02 05:39:57 +07:00
|
|
|
}
|
|
|
|
|
2011-12-17 01:08:53 +07:00
|
|
|
void *kmod_file_get_contents(const struct kmod_file *file)
|
|
|
|
{
|
|
|
|
return file->memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
off_t kmod_file_get_size(const struct kmod_file *file)
|
|
|
|
{
|
|
|
|
return file->size;
|
|
|
|
}
|
|
|
|
|
libkmod: Use kernel decompression when available
With the recent changes to bypass loading the file it's possible to
reduce the work in userspace and delegating it to the kernel. Without
any compression to illustrate:
Before:
read(3, "\177ELF\2\1", 6) = 6
lseek(3, 0, SEEK_SET) = 0
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=238592, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 238592, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd85cbd1000
finit_module(3, "", 0) = 0
munmap(0x7fd85cbd1000, 238592) = 0
close(3) = 0
After:
read(3, "\177ELF\2\1", 6) = 6
lseek(3, 0, SEEK_SET) = 0
finit_module(3, "", 0) = 0
close(3) = 0
When using kernel compression now it's also possible to direct libkmod
to take the finit_module() path, avoiding the decompression in userspace
and just delegating it to the kernel.
Before:
read(3, "(\265/\375\244\0", 6) = 6
lseek(3, 0, SEEK_SET) = 0
read(3, "(\265/\375\244", 5) = 5
mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa431e000
read(3, "\0\244\3\0\\y\6", 7) = 7
mmap(NULL, 372736, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa414f000
brk(0x55944c6a1000) = 0x55944c6a1000
read(3, "\356|\6G\27U\20 \312\260s\211\335\333\263\326\330\336\273O\211\356\306K\360Z\341\374U6\342\221"..., 53038) = 53038
mremap(0x7f3fa431e000, 135168, 266240, MREMAP_MAYMOVE) = 0x7f3fa410e000
read(3, ",;\3\nqf\311\362\325\211\7\341\375A\355\221\371L\\\5\7\375 \32\246<(\258=K\304"..., 20851) = 20851
mremap(0x7f3fa410e000, 266240, 397312, MREMAP_MAYMOVE) = 0x7f3fa40ad000
read(3, ")\36\250\213", 4) = 4
read(3, "", 4) = 0
munmap(0x7f3fa414f000, 372736) = 0
init_module(0x7f3fa40ad010, 238592, "") = 0
munmap(0x7f3fa40ad000, 397312) = 0
close(3) = 0
After:
read(3, "(\265/\375\244P", 6) = 6
lseek(3, 0, SEEK_SET) = 0
finit_module(3, "", 0x4 /* MODULE_INIT_??? */) = 0
close(3) = 0
Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
2023-06-02 05:40:01 +07:00
|
|
|
enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file)
|
2013-02-19 03:02:32 +07:00
|
|
|
{
|
libkmod: Use kernel decompression when available
With the recent changes to bypass loading the file it's possible to
reduce the work in userspace and delegating it to the kernel. Without
any compression to illustrate:
Before:
read(3, "\177ELF\2\1", 6) = 6
lseek(3, 0, SEEK_SET) = 0
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=238592, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 238592, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd85cbd1000
finit_module(3, "", 0) = 0
munmap(0x7fd85cbd1000, 238592) = 0
close(3) = 0
After:
read(3, "\177ELF\2\1", 6) = 6
lseek(3, 0, SEEK_SET) = 0
finit_module(3, "", 0) = 0
close(3) = 0
When using kernel compression now it's also possible to direct libkmod
to take the finit_module() path, avoiding the decompression in userspace
and just delegating it to the kernel.
Before:
read(3, "(\265/\375\244\0", 6) = 6
lseek(3, 0, SEEK_SET) = 0
read(3, "(\265/\375\244", 5) = 5
mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa431e000
read(3, "\0\244\3\0\\y\6", 7) = 7
mmap(NULL, 372736, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa414f000
brk(0x55944c6a1000) = 0x55944c6a1000
read(3, "\356|\6G\27U\20 \312\260s\211\335\333\263\326\330\336\273O\211\356\306K\360Z\341\374U6\342\221"..., 53038) = 53038
mremap(0x7f3fa431e000, 135168, 266240, MREMAP_MAYMOVE) = 0x7f3fa410e000
read(3, ",;\3\nqf\311\362\325\211\7\341\375A\355\221\371L\\\5\7\375 \32\246<(\258=K\304"..., 20851) = 20851
mremap(0x7f3fa410e000, 266240, 397312, MREMAP_MAYMOVE) = 0x7f3fa40ad000
read(3, ")\36\250\213", 4) = 4
read(3, "", 4) = 0
munmap(0x7f3fa414f000, 372736) = 0
init_module(0x7f3fa40ad010, 238592, "") = 0
munmap(0x7f3fa40ad000, 397312) = 0
close(3) = 0
After:
read(3, "(\265/\375\244P", 6) = 6
lseek(3, 0, SEEK_SET) = 0
finit_module(3, "", 0x4 /* MODULE_INIT_??? */) = 0
close(3) = 0
Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
2023-06-02 05:40:01 +07:00
|
|
|
return file->compression;
|
2013-02-19 03:02:32 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int kmod_file_get_fd(const struct kmod_file *file)
|
|
|
|
{
|
|
|
|
return file->fd;
|
|
|
|
}
|
|
|
|
|
2011-12-17 01:08:53 +07:00
|
|
|
void kmod_file_unref(struct kmod_file *file)
|
|
|
|
{
|
2012-10-18 11:36:33 +07:00
|
|
|
if (file->elf)
|
|
|
|
kmod_elf_unref(file->elf);
|
|
|
|
|
2024-02-13 00:23:06 +07:00
|
|
|
if (file->compression == KMOD_FILE_COMPRESSION_NONE) {
|
|
|
|
if (file->memory)
|
|
|
|
munmap(file->memory, file->size);
|
|
|
|
} else {
|
|
|
|
free(file->memory);
|
|
|
|
}
|
2023-06-02 05:39:57 +07:00
|
|
|
|
2024-02-13 00:23:02 +07:00
|
|
|
close(file->fd);
|
2011-12-17 01:08:53 +07:00
|
|
|
free(file);
|
|
|
|
}
|