kmod/libkmod/libkmod-file.c
Emil Velikov 5cd2a2a487 libkmod: update remaining function to return the error code
Rework the function signature and return the error code instead of the
stripped module. Thus we no longer explicitly set errno.

v2:
 - kmod_file_open() - use _cleanup_free_, return errno instead of ENOMEM

Reference: https://github.com/kmod-project/kmod/issues/244
Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
Link: https://github.com/kmod-project/kmod/pull/346
Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
2025-05-19 21:51:44 -05:00

175 lines
3.6 KiB
C

// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright (C) 2011-2013 ProFUSION embedded systems
*/
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <shared/util.h>
#include "libkmod.h"
#include "libkmod-internal.h"
#include "libkmod-internal-file.h"
static const char magic_zstd[] = { 0x28, 0xB5, 0x2F, 0xFD };
static const char magic_xz[] = { 0xfd, '7', 'z', 'X', 'Z', 0 };
static const char magic_zlib[] = { 0x1f, 0x8b };
static int load_reg(struct kmod_file *file)
{
struct stat st;
if (fstat(file->fd, &st) < 0)
return -errno;
file->size = st.st_size;
if ((uintmax_t)file->size > SIZE_MAX)
return -ENOMEM;
file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
if (file->memory == MAP_FAILED) {
file->memory = NULL;
return -errno;
}
return 0;
}
static const struct comp_type {
size_t magic_size;
enum kmod_file_compression_type compression;
const char *magic_bytes;
int (*load)(struct kmod_file *file);
} comp_types[] = {
// clang-format off
{ sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, kmod_file_load_zstd },
{ sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, kmod_file_load_xz },
{ sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, kmod_file_load_zlib },
{ 0, KMOD_FILE_COMPRESSION_NONE, NULL, load_reg },
// clang-format on
};
int kmod_file_get_elf(struct kmod_file *file, struct kmod_elf **elf)
{
if (!file->elf) {
int err = kmod_file_load_contents(file);
if (err)
return err;
err = kmod_elf_new(file->memory, file->size, &file->elf);
if (err)
return err;
}
*elf = file->elf;
return 0;
}
int kmod_file_open(const struct kmod_ctx *ctx, const char *filename,
struct kmod_file **out_file)
{
_cleanup_free_ struct kmod_file *file;
char buf[7];
ssize_t sz;
int ret;
assert_cc(sizeof(magic_zstd) < sizeof(buf));
assert_cc(sizeof(magic_xz) < sizeof(buf));
assert_cc(sizeof(magic_zlib) < sizeof(buf));
file = calloc(1, sizeof(struct kmod_file));
if (file == NULL) {
file = NULL;
return -ENOMEM;
}
file->fd = open(filename, O_RDONLY | O_CLOEXEC);
if (file->fd < 0)
return -errno;
sz = pread_str_safe(file->fd, buf, sizeof(buf), 0);
if (sz != (sizeof(buf) - 1)) {
if (sz < 0)
ret = (int)sz;
else
ret = -EINVAL;
close(file->fd);
return ret;
}
for (unsigned int i = 0; i < ARRAY_SIZE(comp_types); i++) {
const struct comp_type *itr = &comp_types[i];
file->load = itr->load;
file->compression = itr->compression;
if (itr->magic_size &&
memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
break;
}
}
file->ctx = ctx;
*out_file = file;
file = NULL;
return 0;
}
/*
* Callers should just check file->memory got updated
*/
int kmod_file_load_contents(struct kmod_file *file)
{
if (file->memory)
return 0;
/* The load functions already log possible errors. */
return file->load(file);
}
const 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;
}
enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file)
{
return file->compression;
}
int kmod_file_get_fd(const struct kmod_file *file)
{
return file->fd;
}
void kmod_file_unref(struct kmod_file *file)
{
if (file->elf)
kmod_elf_unref(file->elf);
if (file->compression == KMOD_FILE_COMPRESSION_NONE) {
if (file->memory)
munmap(file->memory, file->size);
} else {
free(file->memory);
}
close(file->fd);
free(file);
}