blob: b6a8cc9579f73d38681ee1d4461f9e758c54dbaa [file] [log] [blame]
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -02001/*
2 * libkmod - interface to kernel module operations
3 *
Lucas De Marchie6b0e492013-01-16 11:27:21 -02004 * Copyright (C) 2011-2013 ProFUSION embedded systems
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -02005 *
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
Lucas De Marchidea2dfe2014-12-25 23:32:03 -020017 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020018 */
19
Lucas De Marchic2e42862014-10-03 01:41:42 -030020#include <errno.h>
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010021#include <stdbool.h>
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020022#include <stdio.h>
23#include <stdlib.h>
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020024#include <string.h>
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020025#include <sys/mman.h>
Lucas De Marchic2e42862014-10-03 01:41:42 -030026#include <sys/stat.h>
27#include <sys/types.h>
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020028#include <unistd.h>
Torge Matthies3821e192020-09-08 21:59:20 +020029#ifdef ENABLE_ZSTD
30#include <zstd.h>
31#endif
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010032#ifdef ENABLE_XZ
33#include <lzma.h>
34#endif
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020035#ifdef ENABLE_ZLIB
36#include <zlib.h>
37#endif
38
Lucas De Marchic2e42862014-10-03 01:41:42 -030039#include <shared/util.h>
40
41#include "libkmod.h"
42#include "libkmod-internal.h"
43
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -020044struct kmod_file;
45struct file_ops {
46 int (*load)(struct kmod_file *file);
47 void (*unload)(struct kmod_file *file);
48};
49
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020050struct kmod_file {
Torge Matthies3821e192020-09-08 21:59:20 +020051#ifdef ENABLE_ZSTD
52 bool zstd_used;
53#endif
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010054#ifdef ENABLE_XZ
55 bool xz_used;
56#endif
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020057#ifdef ENABLE_ZLIB
58 gzFile gzf;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020059#endif
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -020060 int fd;
Kees Cook144d1822013-02-18 12:02:32 -080061 bool direct;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020062 off_t size;
63 void *memory;
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -020064 const struct file_ops *ops;
Lucas De Marchic68e92f2012-01-04 08:19:34 -020065 const struct kmod_ctx *ctx;
Lucas De Marchi1eff9422012-10-18 01:36:33 -030066 struct kmod_elf *elf;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020067};
68
Torge Matthies3821e192020-09-08 21:59:20 +020069#ifdef ENABLE_ZSTD
70static int zstd_read_block(struct kmod_file *file, size_t block_size,
71 ZSTD_inBuffer *input, size_t *input_capacity)
72{
73 ssize_t rdret;
74 int ret;
75
76 if (*input_capacity < block_size) {
77 free((void *)input->src);
78 input->src = malloc(block_size);
79 if (input->src == NULL) {
80 ret = -errno;
81 ERR(file->ctx, "zstd: %m\n");
82 return ret;
83 }
84 *input_capacity = block_size;
85 }
86
87 rdret = read(file->fd, (void *)input->src, block_size);
88 if (rdret < 0) {
89 ret = -errno;
90 ERR(file->ctx, "zstd: %m\n");
91 return ret;
92 }
93
94 input->pos = 0;
95 input->size = rdret;
96 return 0;
97}
98
99static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free)
100{
101 uint8_t *old_buffer = buffer->dst;
102 int ret = 0;
103
104 if (buffer->size - buffer->pos >= min_free)
105 return 0;
106
107 buffer->size += min_free;
108 buffer->dst = realloc(buffer->dst, buffer->size);
109 if (buffer->dst == NULL) {
110 ret = -errno;
111 free(old_buffer);
112 }
113
114 return ret;
115}
116
117static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr,
118 ZSTD_inBuffer *input, ZSTD_outBuffer *output,
119 size_t *next_block_size)
120{
121 size_t out_buf_min_size = ZSTD_DStreamOutSize();
122 int ret = 0;
123
124 do {
125 ssize_t dsret;
126
127 ret = zstd_ensure_outbuffer_space(output, out_buf_min_size);
128 if (ret) {
129 ERR(file->ctx, "zstd: %s\n", strerror(-ret));
130 break;
131 }
132
133 dsret = ZSTD_decompressStream(dstr, output, input);
134 if (ZSTD_isError(dsret)) {
135 ret = -EINVAL;
136 ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret));
137 break;
138 }
139 if (dsret > 0)
140 *next_block_size = (size_t)dsret;
141 } while (input->pos < input->size
142 || output->pos > output->size
143 || output->size - output->pos < out_buf_min_size);
144
145 return ret;
146}
147
148static int load_zstd(struct kmod_file *file)
149{
150 ZSTD_DStream *dstr;
151 size_t next_block_size;
152 size_t zst_inb_capacity = 0;
153 ZSTD_inBuffer zst_inb = { 0 };
154 ZSTD_outBuffer zst_outb = { 0 };
155 int ret;
156
157 dstr = ZSTD_createDStream();
158 if (dstr == NULL) {
159 ret = -EINVAL;
160 ERR(file->ctx, "zstd: Failed to create decompression stream\n");
161 goto out;
162 }
163
164 next_block_size = ZSTD_initDStream(dstr);
165
166 while (true) {
167 ret = zstd_read_block(file, next_block_size, &zst_inb,
168 &zst_inb_capacity);
169 if (ret != 0)
170 goto out;
171 if (zst_inb.size == 0) /* EOF */
172 break;
173
174 ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb,
175 &next_block_size);
176 if (ret != 0)
177 goto out;
178 }
179
180 ZSTD_freeDStream(dstr);
181 free((void *)zst_inb.src);
182 file->zstd_used = true;
183 file->memory = zst_outb.dst;
184 file->size = zst_outb.pos;
185 return 0;
186out:
187 if (dstr != NULL)
188 ZSTD_freeDStream(dstr);
189 free((void *)zst_inb.src);
190 free((void *)zst_outb.dst);
191 return ret;
192}
193
194static void unload_zstd(struct kmod_file *file)
195{
196 if (!file->zstd_used)
197 return;
198 free(file->memory);
199}
200
201static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
202#endif
203
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100204#ifdef ENABLE_XZ
Lucas De Marchi3f635052012-01-04 08:23:15 -0200205static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100206{
207 switch (ret) {
208 case LZMA_MEM_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -0200209 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100210 break;
211 case LZMA_FORMAT_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -0200212 ERR(file->ctx, "xz: File format not recognized\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100213 break;
214 case LZMA_OPTIONS_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -0200215 ERR(file->ctx, "xz: Unsupported compression options\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100216 break;
217 case LZMA_DATA_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -0200218 ERR(file->ctx, "xz: File is corrupt\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100219 break;
220 case LZMA_BUF_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -0200221 ERR(file->ctx, "xz: Unexpected end of input\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100222 break;
223 default:
Lucas De Marchi3f635052012-01-04 08:23:15 -0200224 ERR(file->ctx, "xz: Internal error (bug)\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100225 break;
226 }
227}
228
229static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
230{
231 uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
232 lzma_action action = LZMA_RUN;
233 lzma_ret ret;
234 void *p = NULL;
235 size_t total = 0;
236
237 strm->avail_in = 0;
238 strm->next_out = out_buf;
239 strm->avail_out = sizeof(out_buf);
240
241 while (true) {
242 if (strm->avail_in == 0) {
243 ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
244 if (rdret < 0) {
245 ret = -errno;
246 goto out;
247 }
248 strm->next_in = in_buf;
249 strm->avail_in = rdret;
250 if (rdret == 0)
251 action = LZMA_FINISH;
252 }
253 ret = lzma_code(strm, action);
254 if (strm->avail_out == 0 || ret != LZMA_OK) {
255 size_t write_size = BUFSIZ - strm->avail_out;
256 char *tmp = realloc(p, total + write_size);
257 if (tmp == NULL) {
258 ret = -errno;
259 goto out;
260 }
261 memcpy(tmp + total, out_buf, write_size);
262 total += write_size;
263 p = tmp;
264 strm->next_out = out_buf;
265 strm->avail_out = BUFSIZ;
266 }
267 if (ret == LZMA_STREAM_END)
268 break;
269 if (ret != LZMA_OK) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200270 xz_uncompress_belch(file, ret);
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100271 ret = -EINVAL;
272 goto out;
273 }
274 }
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200275 file->xz_used = true;
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100276 file->memory = p;
277 file->size = total;
278 return 0;
279 out:
280 free(p);
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100281 return ret;
282}
283
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200284static int load_xz(struct kmod_file *file)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100285{
286 lzma_stream strm = LZMA_STREAM_INIT;
287 lzma_ret lzret;
288 int ret;
289
290 lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
291 if (lzret == LZMA_MEM_ERROR) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200292 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100293 return -ENOMEM;
294 } else if (lzret != LZMA_OK) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200295 ERR(file->ctx, "xz: Internal error (bug)\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100296 return -EINVAL;
297 }
298 ret = xz_uncompress(&strm, file);
299 lzma_end(&strm);
300 return ret;
301}
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200302
303static void unload_xz(struct kmod_file *file)
304{
305 if (!file->xz_used)
306 return;
307 free(file->memory);
308}
309
310static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100311#endif
312
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200313#ifdef ENABLE_ZLIB
314#define READ_STEP (4 * 1024 * 1024)
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200315static int load_zlib(struct kmod_file *file)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200316{
317 int err = 0;
318 off_t did = 0, total = 0;
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200319 _cleanup_free_ unsigned char *p = NULL;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200320
321 errno = 0;
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200322 file->gzf = gzdopen(file->fd, "rb");
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200323 if (file->gzf == NULL)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200324 return -errno;
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200325 file->fd = -1; /* now owned by gzf due gzdopen() */
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200326
327 for (;;) {
328 int r;
329
330 if (did == total) {
331 void *tmp = realloc(p, total + READ_STEP);
332 if (tmp == NULL) {
333 err = -errno;
334 goto error;
335 }
336 total += READ_STEP;
337 p = tmp;
338 }
339
340 r = gzread(file->gzf, p + did, total - did);
341 if (r == 0)
342 break;
343 else if (r < 0) {
Dave Reisnerc7d5a602012-05-07 19:41:41 -0400344 int gzerr;
345 const char *gz_errmsg = gzerror(file->gzf, &gzerr);
346
347 ERR(file->ctx, "gzip: %s\n", gz_errmsg);
348
349 /* gzip might not set errno here */
350 err = gzerr == Z_ERRNO ? -errno : -EINVAL;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200351 goto error;
352 }
353 did += r;
354 }
355
356 file->memory = p;
357 file->size = did;
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200358 p = NULL;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200359 return 0;
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200360
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200361error:
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200362 gzclose(file->gzf);
363 return err;
364}
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200365
366static void unload_zlib(struct kmod_file *file)
367{
368 if (file->gzf == NULL)
369 return;
370 free(file->memory);
371 gzclose(file->gzf); /* closes file->fd */
372}
373
374static const char magic_zlib[] = {0x1f, 0x8b};
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200375#endif
376
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200377static const struct comp_type {
378 size_t magic_size;
379 const char *magic_bytes;
380 const struct file_ops ops;
381} comp_types[] = {
Torge Matthies3821e192020-09-08 21:59:20 +0200382#ifdef ENABLE_ZSTD
383 {sizeof(magic_zstd), magic_zstd, {load_zstd, unload_zstd}},
384#endif
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200385#ifdef ENABLE_XZ
386 {sizeof(magic_xz), magic_xz, {load_xz, unload_xz}},
387#endif
388#ifdef ENABLE_ZLIB
389 {sizeof(magic_zlib), magic_zlib, {load_zlib, unload_zlib}},
390#endif
391 {0, NULL, {NULL, NULL}}
392};
393
394static int load_reg(struct kmod_file *file)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200395{
396 struct stat st;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200397
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200398 if (fstat(file->fd, &st) < 0)
399 return -errno;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200400
401 file->size = st.st_size;
Kees Cookc3e8d262013-02-18 12:02:34 -0800402 file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
403 file->fd, 0);
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200404 if (file->memory == MAP_FAILED)
405 return -errno;
Kees Cook144d1822013-02-18 12:02:32 -0800406 file->direct = true;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200407 return 0;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200408}
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200409
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200410static void unload_reg(struct kmod_file *file)
411{
412 munmap(file->memory, file->size);
413}
414
Lucas De Marchi7749bed2012-01-04 08:00:45 -0200415static const struct file_ops reg_ops = {
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200416 load_reg, unload_reg
417};
418
Lucas De Marchi1eff9422012-10-18 01:36:33 -0300419struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
420{
421 if (file->elf)
422 return file->elf;
423
424 file->elf = kmod_elf_new(file->memory, file->size);
425 return file->elf;
426}
427
Lucas De Marchic68e92f2012-01-04 08:19:34 -0200428struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
429 const char *filename)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200430{
431 struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200432 const struct comp_type *itr;
433 size_t magic_size_max = 0;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200434 int err;
435
436 if (file == NULL)
437 return NULL;
438
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200439 file->fd = open(filename, O_RDONLY|O_CLOEXEC);
440 if (file->fd < 0) {
441 err = -errno;
442 goto error;
443 }
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200444
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200445 for (itr = comp_types; itr->ops.load != NULL; itr++) {
446 if (magic_size_max < itr->magic_size)
447 magic_size_max = itr->magic_size;
448 }
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200449
Kees Cook144d1822013-02-18 12:02:32 -0800450 file->direct = false;
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200451 if (magic_size_max > 0) {
452 char *buf = alloca(magic_size_max + 1);
453 ssize_t sz;
454
455 if (buf == NULL) {
456 err = -errno;
457 goto error;
458 }
459 sz = read_str_safe(file->fd, buf, magic_size_max + 1);
460 lseek(file->fd, 0, SEEK_SET);
461 if (sz != (ssize_t)magic_size_max) {
462 if (sz < 0)
463 err = sz;
464 else
465 err = -EINVAL;
466 goto error;
467 }
468
469 for (itr = comp_types; itr->ops.load != NULL; itr++) {
470 if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0)
471 break;
472 }
473 if (itr->ops.load != NULL)
474 file->ops = &itr->ops;
475 }
476
477 if (file->ops == NULL)
478 file->ops = &reg_ops;
479
480 err = file->ops->load(file);
Lucas De Marchic68e92f2012-01-04 08:19:34 -0200481 file->ctx = ctx;
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200482error:
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200483 if (err < 0) {
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200484 if (file->fd >= 0)
485 close(file->fd);
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200486 free(file);
487 errno = -err;
488 return NULL;
489 }
490
491 return file;
492}
493
494void *kmod_file_get_contents(const struct kmod_file *file)
495{
496 return file->memory;
497}
498
499off_t kmod_file_get_size(const struct kmod_file *file)
500{
501 return file->size;
502}
503
Kees Cook144d1822013-02-18 12:02:32 -0800504bool kmod_file_get_direct(const struct kmod_file *file)
505{
506 return file->direct;
507}
508
509int kmod_file_get_fd(const struct kmod_file *file)
510{
511 return file->fd;
512}
513
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200514void kmod_file_unref(struct kmod_file *file)
515{
Lucas De Marchi1eff9422012-10-18 01:36:33 -0300516 if (file->elf)
517 kmod_elf_unref(file->elf);
518
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200519 file->ops->unload(file);
520 if (file->fd >= 0)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100521 close(file->fd);
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200522 free(file);
523}