1/* 2 * libkmod - interface to kernel module operations 3 * 4 * Copyright (C) 2011-2013 ProFUSION embedded systems 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include <errno.h> 21#include <stdbool.h> 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include <sys/mman.h> 26#include <sys/stat.h> 27#include <sys/types.h> 28#include <unistd.h> 29#ifdef ENABLE_XZ 30#include <lzma.h> 31#endif 32#ifdef ENABLE_ZLIB 33#include <zlib.h> 34#endif 35 36#include <shared/util.h> 37 38#include "libkmod.h" 39#include "libkmod-internal.h" 40 41struct kmod_file; 42struct file_ops { 43 int (*load)(struct kmod_file *file); 44 void (*unload)(struct kmod_file *file); 45}; 46 47struct kmod_file { 48#ifdef ENABLE_XZ 49 bool xz_used; 50#endif 51#ifdef ENABLE_ZLIB 52 gzFile gzf; 53#endif 54 int fd; 55 bool direct; 56 off_t size; 57 void *memory; 58 const struct file_ops *ops; 59 const struct kmod_ctx *ctx; 60 struct kmod_elf *elf; 61}; 62 63#ifdef ENABLE_XZ 64static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret) 65{ 66 switch (ret) { 67 case LZMA_MEM_ERROR: 68 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM)); 69 break; 70 case LZMA_FORMAT_ERROR: 71 ERR(file->ctx, "xz: File format not recognized\n"); 72 break; 73 case LZMA_OPTIONS_ERROR: 74 ERR(file->ctx, "xz: Unsupported compression options\n"); 75 break; 76 case LZMA_DATA_ERROR: 77 ERR(file->ctx, "xz: File is corrupt\n"); 78 break; 79 case LZMA_BUF_ERROR: 80 ERR(file->ctx, "xz: Unexpected end of input\n"); 81 break; 82 default: 83 ERR(file->ctx, "xz: Internal error (bug)\n"); 84 break; 85 } 86} 87 88static int xz_uncompress(lzma_stream *strm, struct kmod_file *file) 89{ 90 uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ]; 91 lzma_action action = LZMA_RUN; 92 lzma_ret ret; 93 void *p = NULL; 94 size_t total = 0; 95 96 strm->avail_in = 0; 97 strm->next_out = out_buf; 98 strm->avail_out = sizeof(out_buf); 99 100 while (true) { 101 if (strm->avail_in == 0) { 102 ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf)); 103 if (rdret < 0) { 104 ret = -errno; 105 goto out; 106 } 107 strm->next_in = in_buf; 108 strm->avail_in = rdret; 109 if (rdret == 0) 110 action = LZMA_FINISH; 111 } 112 ret = lzma_code(strm, action); 113 if (strm->avail_out == 0 || ret != LZMA_OK) { 114 size_t write_size = BUFSIZ - strm->avail_out; 115 char *tmp = realloc(p, total + write_size); 116 if (tmp == NULL) { 117 ret = -errno; 118 goto out; 119 } 120 memcpy(tmp + total, out_buf, write_size); 121 total += write_size; 122 p = tmp; 123 strm->next_out = out_buf; 124 strm->avail_out = BUFSIZ; 125 } 126 if (ret == LZMA_STREAM_END) 127 break; 128 if (ret != LZMA_OK) { 129 xz_uncompress_belch(file, ret); 130 ret = -EINVAL; 131 goto out; 132 } 133 } 134 file->xz_used = true; 135 file->memory = p; 136 file->size = total; 137 return 0; 138 out: 139 free(p); 140 return ret; 141} 142 143static int load_xz(struct kmod_file *file) 144{ 145 lzma_stream strm = LZMA_STREAM_INIT; 146 lzma_ret lzret; 147 int ret; 148 149 lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); 150 if (lzret == LZMA_MEM_ERROR) { 151 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM)); 152 return -ENOMEM; 153 } else if (lzret != LZMA_OK) { 154 ERR(file->ctx, "xz: Internal error (bug)\n"); 155 return -EINVAL; 156 } 157 ret = xz_uncompress(&strm, file); 158 lzma_end(&strm); 159 return ret; 160} 161 162static void unload_xz(struct kmod_file *file) 163{ 164 if (!file->xz_used) 165 return; 166 free(file->memory); 167} 168 169static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0}; 170#endif 171 172#ifdef ENABLE_ZLIB 173#define READ_STEP (4 * 1024 * 1024) 174static int load_zlib(struct kmod_file *file) 175{ 176 int err = 0; 177 off_t did = 0, total = 0; 178 _cleanup_free_ unsigned char *p = NULL; 179 180 errno = 0; 181 file->gzf = gzdopen(file->fd, "rb"); 182 if (file->gzf == NULL) 183 return -errno; 184 file->fd = -1; /* now owned by gzf due gzdopen() */ 185 186 for (;;) { 187 int r; 188 189 if (did == total) { 190 void *tmp = realloc(p, total + READ_STEP); 191 if (tmp == NULL) { 192 err = -errno; 193 goto error; 194 } 195 total += READ_STEP; 196 p = tmp; 197 } 198 199 r = gzread(file->gzf, p + did, total - did); 200 if (r == 0) 201 break; 202 else if (r < 0) { 203 int gzerr; 204 const char *gz_errmsg = gzerror(file->gzf, &gzerr); 205 206 ERR(file->ctx, "gzip: %s\n", gz_errmsg); 207 208 /* gzip might not set errno here */ 209 err = gzerr == Z_ERRNO ? -errno : -EINVAL; 210 goto error; 211 } 212 did += r; 213 } 214 215 file->memory = p; 216 file->size = did; 217 p = NULL; 218 return 0; 219 220error: 221 gzclose(file->gzf); 222 return err; 223} 224 225static void unload_zlib(struct kmod_file *file) 226{ 227 if (file->gzf == NULL) 228 return; 229 free(file->memory); 230 gzclose(file->gzf); /* closes file->fd */ 231} 232 233static const char magic_zlib[] = {0x1f, 0x8b}; 234#endif 235 236static const struct comp_type { 237 size_t magic_size; 238 const char *magic_bytes; 239 const struct file_ops ops; 240} comp_types[] = { 241#ifdef ENABLE_XZ 242 {sizeof(magic_xz), magic_xz, {load_xz, unload_xz}}, 243#endif 244#ifdef ENABLE_ZLIB 245 {sizeof(magic_zlib), magic_zlib, {load_zlib, unload_zlib}}, 246#endif 247 {0, NULL, {NULL, NULL}} 248}; 249 250static int load_reg(struct kmod_file *file) 251{ 252 struct stat st; 253 254 if (fstat(file->fd, &st) < 0) 255 return -errno; 256 257 file->size = st.st_size; 258 file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE, 259 file->fd, 0); 260 if (file->memory == MAP_FAILED) 261 return -errno; 262 file->direct = true; 263 return 0; 264} 265 266static void unload_reg(struct kmod_file *file) 267{ 268 munmap(file->memory, file->size); 269} 270 271static const struct file_ops reg_ops = { 272 load_reg, unload_reg 273}; 274 275struct kmod_elf *kmod_file_get_elf(struct kmod_file *file) 276{ 277 if (file->elf) 278 return file->elf; 279 280 file->elf = kmod_elf_new(file->memory, file->size); 281 return file->elf; 282} 283 284struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, 285 const char *filename) 286{ 287 struct kmod_file *file = calloc(1, sizeof(struct kmod_file)); 288 const struct comp_type *itr; 289 size_t magic_size_max = 0; 290 int err; 291 292 if (file == NULL) 293 return NULL; 294 295 file->fd = open(filename, O_RDONLY|O_CLOEXEC); 296 if (file->fd < 0) { 297 err = -errno; 298 goto error; 299 } 300 301 for (itr = comp_types; itr->ops.load != NULL; itr++) { 302 if (magic_size_max < itr->magic_size) 303 magic_size_max = itr->magic_size; 304 } 305 306 file->direct = false; 307 if (magic_size_max > 0) { 308 char *buf = alloca(magic_size_max + 1); 309 ssize_t sz; 310 311 if (buf == NULL) { 312 err = -errno; 313 goto error; 314 } 315 sz = read_str_safe(file->fd, buf, magic_size_max + 1); 316 lseek(file->fd, 0, SEEK_SET); 317 if (sz != (ssize_t)magic_size_max) { 318 if (sz < 0) 319 err = sz; 320 else 321 err = -EINVAL; 322 goto error; 323 } 324 325 for (itr = comp_types; itr->ops.load != NULL; itr++) { 326 if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) 327 break; 328 } 329 if (itr->ops.load != NULL) 330 file->ops = &itr->ops; 331 } 332 333 if (file->ops == NULL) 334 file->ops = ®_ops; 335 336 err = file->ops->load(file); 337 file->ctx = ctx; 338error: 339 if (err < 0) { 340 if (file->fd >= 0) 341 close(file->fd); 342 free(file); 343 errno = -err; 344 return NULL; 345 } 346 347 return file; 348} 349 350void *kmod_file_get_contents(const struct kmod_file *file) 351{ 352 return file->memory; 353} 354 355off_t kmod_file_get_size(const struct kmod_file *file) 356{ 357 return file->size; 358} 359 360bool kmod_file_get_direct(const struct kmod_file *file) 361{ 362 return file->direct; 363} 364 365int kmod_file_get_fd(const struct kmod_file *file) 366{ 367 return file->fd; 368} 369 370void kmod_file_unref(struct kmod_file *file) 371{ 372 if (file->elf) 373 kmod_elf_unref(file->elf); 374 375 file->ops->unload(file); 376 if (file->fd >= 0) 377 close(file->fd); 378 free(file); 379} 380