Home | History | Annotate | Download | only in libkmod
      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 
     41 struct kmod_file;
     42 struct file_ops {
     43 	int (*load)(struct kmod_file *file);
     44 	void (*unload)(struct kmod_file *file);
     45 };
     46 
     47 struct 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
     64 static 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 
     88 static 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 
    143 static 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 
    162 static void unload_xz(struct kmod_file *file)
    163 {
    164 	if (!file->xz_used)
    165 		return;
    166 	free(file->memory);
    167 }
    168 
    169 static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
    170 #endif
    171 
    172 #ifdef ENABLE_ZLIB
    173 #define READ_STEP (4 * 1024 * 1024)
    174 static 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 
    220 error:
    221 	gzclose(file->gzf);
    222 	return err;
    223 }
    224 
    225 static 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 
    233 static const char magic_zlib[] = {0x1f, 0x8b};
    234 #endif
    235 
    236 static 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 
    250 static 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 
    266 static void unload_reg(struct kmod_file *file)
    267 {
    268 	munmap(file->memory, file->size);
    269 }
    270 
    271 static const struct file_ops reg_ops = {
    272 	load_reg, unload_reg
    273 };
    274 
    275 struct 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 
    284 struct 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 = &reg_ops;
    335 
    336 	err = file->ops->load(file);
    337 	file->ctx = ctx;
    338 error:
    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 
    350 void *kmod_file_get_contents(const struct kmod_file *file)
    351 {
    352 	return file->memory;
    353 }
    354 
    355 off_t kmod_file_get_size(const struct kmod_file *file)
    356 {
    357 	return file->size;
    358 }
    359 
    360 bool kmod_file_get_direct(const struct kmod_file *file)
    361 {
    362 	return file->direct;
    363 }
    364 
    365 int kmod_file_get_fd(const struct kmod_file *file)
    366 {
    367 	return file->fd;
    368 }
    369 
    370 void 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