• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
xz_uncompress_belch(struct kmod_file * file,lzma_ret ret)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 
xz_uncompress(lzma_stream * strm,struct kmod_file * file)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 
load_xz(struct kmod_file * file)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 
unload_xz(struct kmod_file * file)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)
load_zlib(struct kmod_file * file)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 
unload_zlib(struct kmod_file * file)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 
load_reg(struct kmod_file * file)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 
unload_reg(struct kmod_file * file)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 
kmod_file_get_elf(struct kmod_file * file)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 
kmod_file_open(const struct kmod_ctx * ctx,const char * filename)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 
kmod_file_get_contents(const struct kmod_file * file)350 void *kmod_file_get_contents(const struct kmod_file *file)
351 {
352 	return file->memory;
353 }
354 
kmod_file_get_size(const struct kmod_file * file)355 off_t kmod_file_get_size(const struct kmod_file *file)
356 {
357 	return file->size;
358 }
359 
kmod_file_get_direct(const struct kmod_file * file)360 bool kmod_file_get_direct(const struct kmod_file *file)
361 {
362 	return file->direct;
363 }
364 
kmod_file_get_fd(const struct kmod_file * file)365 int kmod_file_get_fd(const struct kmod_file *file)
366 {
367 	return file->fd;
368 }
369 
kmod_file_unref(struct kmod_file * file)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