| // 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); |
| } |