1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
// 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
};
struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
{
int err;
if (file->elf)
return file->elf;
err = kmod_file_load_contents(file);
if (err) {
errno = -err;
return NULL;
}
file->elf = kmod_elf_new(file->memory, file->size);
return file->elf;
}
struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, const char *filename)
{
struct kmod_file *file;
char buf[7];
ssize_t sz;
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)
return NULL;
file->fd = open(filename, O_RDONLY | O_CLOEXEC);
if (file->fd < 0) {
free(file);
return NULL;
}
sz = pread_str_safe(file->fd, buf, sizeof(buf), 0);
if (sz != (sizeof(buf) - 1)) {
if (sz < 0)
errno = -sz;
else
errno = EINVAL;
close(file->fd);
free(file);
return NULL;
}
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;
return file;
}
/*
* 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);
}
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);
}
|