]> git.ipfire.org Git - thirdparty/kmod.git/blame - libkmod/libkmod-file.c
libkmod: keep KMOD_FILE_COMPRESSION_NONE/load_reg in comp_types
[thirdparty/kmod.git] / libkmod / libkmod-file.c
CommitLineData
3d8226ed
GSB
1/*
2 * libkmod - interface to kernel module operations
3 *
e6b0e49b 4 * Copyright (C) 2011-2013 ProFUSION embedded systems
3d8226ed
GSB
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
dea2dfee 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
3d8226ed
GSB
18 */
19
c2e4286b 20#include <errno.h>
b182f8fb 21#include <stdbool.h>
3d8226ed
GSB
22#include <stdio.h>
23#include <stdlib.h>
3d8226ed 24#include <string.h>
3d8226ed 25#include <sys/mman.h>
c2e4286b
LDM
26#include <sys/stat.h>
27#include <sys/types.h>
3d8226ed 28#include <unistd.h>
3821e197
TM
29#ifdef ENABLE_ZSTD
30#include <zstd.h>
31#endif
b182f8fb
JE
32#ifdef ENABLE_XZ
33#include <lzma.h>
34#endif
3d8226ed
GSB
35#ifdef ENABLE_ZLIB
36#include <zlib.h>
37#endif
38
c2e4286b
LDM
39#include <shared/util.h>
40
41#include "libkmod.h"
42#include "libkmod-internal.h"
43
3d8226ed 44struct kmod_file {
bb417099 45 int fd;
e5398276 46 enum kmod_file_compression_type compression;
3d8226ed
GSB
47 off_t size;
48 void *memory;
ad158923 49 int (*load)(struct kmod_file *file);
c68e92f7 50 const struct kmod_ctx *ctx;
1eff942e 51 struct kmod_elf *elf;
3d8226ed
GSB
52};
53
3821e197
TM
54#ifdef ENABLE_ZSTD
55static int zstd_read_block(struct kmod_file *file, size_t block_size,
56 ZSTD_inBuffer *input, size_t *input_capacity)
57{
58 ssize_t rdret;
59 int ret;
60
61 if (*input_capacity < block_size) {
62 free((void *)input->src);
63 input->src = malloc(block_size);
64 if (input->src == NULL) {
65 ret = -errno;
66 ERR(file->ctx, "zstd: %m\n");
67 return ret;
68 }
69 *input_capacity = block_size;
70 }
71
72 rdret = read(file->fd, (void *)input->src, block_size);
73 if (rdret < 0) {
74 ret = -errno;
75 ERR(file->ctx, "zstd: %m\n");
76 return ret;
77 }
78
79 input->pos = 0;
80 input->size = rdret;
81 return 0;
82}
83
84static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free)
85{
86 uint8_t *old_buffer = buffer->dst;
87 int ret = 0;
88
89 if (buffer->size - buffer->pos >= min_free)
90 return 0;
91
92 buffer->size += min_free;
93 buffer->dst = realloc(buffer->dst, buffer->size);
94 if (buffer->dst == NULL) {
95 ret = -errno;
96 free(old_buffer);
97 }
98
99 return ret;
100}
101
102static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr,
103 ZSTD_inBuffer *input, ZSTD_outBuffer *output,
104 size_t *next_block_size)
105{
106 size_t out_buf_min_size = ZSTD_DStreamOutSize();
107 int ret = 0;
108
109 do {
110 ssize_t dsret;
111
112 ret = zstd_ensure_outbuffer_space(output, out_buf_min_size);
113 if (ret) {
114 ERR(file->ctx, "zstd: %s\n", strerror(-ret));
115 break;
116 }
117
118 dsret = ZSTD_decompressStream(dstr, output, input);
119 if (ZSTD_isError(dsret)) {
120 ret = -EINVAL;
121 ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret));
122 break;
123 }
124 if (dsret > 0)
125 *next_block_size = (size_t)dsret;
126 } while (input->pos < input->size
127 || output->pos > output->size
128 || output->size - output->pos < out_buf_min_size);
129
130 return ret;
131}
132
133static int load_zstd(struct kmod_file *file)
134{
135 ZSTD_DStream *dstr;
136 size_t next_block_size;
137 size_t zst_inb_capacity = 0;
138 ZSTD_inBuffer zst_inb = { 0 };
139 ZSTD_outBuffer zst_outb = { 0 };
140 int ret;
141
142 dstr = ZSTD_createDStream();
143 if (dstr == NULL) {
144 ret = -EINVAL;
145 ERR(file->ctx, "zstd: Failed to create decompression stream\n");
146 goto out;
147 }
148
149 next_block_size = ZSTD_initDStream(dstr);
150
151 while (true) {
152 ret = zstd_read_block(file, next_block_size, &zst_inb,
153 &zst_inb_capacity);
154 if (ret != 0)
155 goto out;
156 if (zst_inb.size == 0) /* EOF */
157 break;
158
159 ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb,
160 &next_block_size);
161 if (ret != 0)
162 goto out;
163 }
164
165 ZSTD_freeDStream(dstr);
166 free((void *)zst_inb.src);
3821e197
TM
167 file->memory = zst_outb.dst;
168 file->size = zst_outb.pos;
169 return 0;
170out:
171 if (dstr != NULL)
172 ZSTD_freeDStream(dstr);
173 free((void *)zst_inb.src);
174 free((void *)zst_outb.dst);
175 return ret;
176}
0c127388
EV
177#else
178static int load_zstd(struct kmod_file *file)
179{
180 return -ENOSYS;
181}
182#endif
3821e197 183
3821e197 184static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
3821e197 185
b182f8fb 186#ifdef ENABLE_XZ
3f635058 187static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
b182f8fb
JE
188{
189 switch (ret) {
190 case LZMA_MEM_ERROR:
3f635058 191 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
b182f8fb
JE
192 break;
193 case LZMA_FORMAT_ERROR:
3f635058 194 ERR(file->ctx, "xz: File format not recognized\n");
b182f8fb
JE
195 break;
196 case LZMA_OPTIONS_ERROR:
3f635058 197 ERR(file->ctx, "xz: Unsupported compression options\n");
b182f8fb
JE
198 break;
199 case LZMA_DATA_ERROR:
3f635058 200 ERR(file->ctx, "xz: File is corrupt\n");
b182f8fb
JE
201 break;
202 case LZMA_BUF_ERROR:
3f635058 203 ERR(file->ctx, "xz: Unexpected end of input\n");
b182f8fb
JE
204 break;
205 default:
3f635058 206 ERR(file->ctx, "xz: Internal error (bug)\n");
b182f8fb
JE
207 break;
208 }
209}
210
211static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
212{
213 uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
214 lzma_action action = LZMA_RUN;
215 lzma_ret ret;
216 void *p = NULL;
217 size_t total = 0;
218
219 strm->avail_in = 0;
220 strm->next_out = out_buf;
221 strm->avail_out = sizeof(out_buf);
222
223 while (true) {
224 if (strm->avail_in == 0) {
225 ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
226 if (rdret < 0) {
227 ret = -errno;
228 goto out;
229 }
230 strm->next_in = in_buf;
231 strm->avail_in = rdret;
232 if (rdret == 0)
233 action = LZMA_FINISH;
234 }
235 ret = lzma_code(strm, action);
236 if (strm->avail_out == 0 || ret != LZMA_OK) {
237 size_t write_size = BUFSIZ - strm->avail_out;
238 char *tmp = realloc(p, total + write_size);
239 if (tmp == NULL) {
240 ret = -errno;
241 goto out;
242 }
243 memcpy(tmp + total, out_buf, write_size);
244 total += write_size;
245 p = tmp;
246 strm->next_out = out_buf;
247 strm->avail_out = BUFSIZ;
248 }
249 if (ret == LZMA_STREAM_END)
250 break;
251 if (ret != LZMA_OK) {
3f635058 252 xz_uncompress_belch(file, ret);
b182f8fb
JE
253 ret = -EINVAL;
254 goto out;
255 }
256 }
257 file->memory = p;
258 file->size = total;
259 return 0;
260 out:
261 free(p);
b182f8fb
JE
262 return ret;
263}
264
db5d14cf 265static int load_xz(struct kmod_file *file)
b182f8fb
JE
266{
267 lzma_stream strm = LZMA_STREAM_INIT;
268 lzma_ret lzret;
269 int ret;
270
271 lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
272 if (lzret == LZMA_MEM_ERROR) {
3f635058 273 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
b182f8fb
JE
274 return -ENOMEM;
275 } else if (lzret != LZMA_OK) {
3f635058 276 ERR(file->ctx, "xz: Internal error (bug)\n");
b182f8fb
JE
277 return -EINVAL;
278 }
279 ret = xz_uncompress(&strm, file);
280 lzma_end(&strm);
281 return ret;
282}
0c127388
EV
283#else
284static int load_xz(struct kmod_file *file)
285{
286 return -ENOSYS;
287}
288#endif
b182f8fb 289
db5d14cf 290static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
db5d14cf
GSB
291
292#ifdef ENABLE_ZLIB
293#define READ_STEP (4 * 1024 * 1024)
294static int load_zlib(struct kmod_file *file)
3d8226ed
GSB
295{
296 int err = 0;
297 off_t did = 0, total = 0;
d3c16c79 298 _cleanup_free_ unsigned char *p = NULL;
09256b9a 299 gzFile gzf;
d6cd6c74 300 int gzfd;
3d8226ed
GSB
301
302 errno = 0;
d6cd6c74
EV
303 gzfd = fcntl(file->fd, F_DUPFD_CLOEXEC, 3);
304 if (gzfd < 0)
305 return -errno;
306
09256b9a
EV
307 gzf = gzdopen(gzfd, "rb"); /* takes ownership of the fd */
308 if (gzf == NULL) {
d6cd6c74 309 close(gzfd);
3d8226ed 310 return -errno;
d6cd6c74 311 }
3d8226ed
GSB
312
313 for (;;) {
314 int r;
315
316 if (did == total) {
317 void *tmp = realloc(p, total + READ_STEP);
318 if (tmp == NULL) {
319 err = -errno;
320 goto error;
321 }
322 total += READ_STEP;
323 p = tmp;
324 }
325
09256b9a 326 r = gzread(gzf, p + did, total - did);
3d8226ed
GSB
327 if (r == 0)
328 break;
329 else if (r < 0) {
c7d5a60d 330 int gzerr;
09256b9a 331 const char *gz_errmsg = gzerror(gzf, &gzerr);
c7d5a60d
DR
332
333 ERR(file->ctx, "gzip: %s\n", gz_errmsg);
334
335 /* gzip might not set errno here */
336 err = gzerr == Z_ERRNO ? -errno : -EINVAL;
3d8226ed
GSB
337 goto error;
338 }
339 did += r;
340 }
341
342 file->memory = p;
343 file->size = did;
d3c16c79 344 p = NULL;
09256b9a 345 gzclose(gzf);
3d8226ed 346 return 0;
d3c16c79 347
3d8226ed 348error:
09256b9a 349 gzclose(gzf); /* closes the gzfd */
3d8226ed
GSB
350 return err;
351}
0c127388
EV
352#else
353static int load_zlib(struct kmod_file *file)
354{
355 return -ENOSYS;
356}
357#endif
db5d14cf 358
db5d14cf 359static const char magic_zlib[] = {0x1f, 0x8b};
db5d14cf 360
db5d14cf 361static int load_reg(struct kmod_file *file)
3d8226ed
GSB
362{
363 struct stat st;
3d8226ed 364
db5d14cf
GSB
365 if (fstat(file->fd, &st) < 0)
366 return -errno;
3d8226ed
GSB
367
368 file->size = st.st_size;
c3e8d269
KC
369 file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
370 file->fd, 0);
90b271fb
EV
371 if (file->memory == MAP_FAILED) {
372 file->memory = NULL;
db5d14cf 373 return -errno;
90b271fb 374 }
e5398276 375
3d8226ed 376 return 0;
3d8226ed 377}
3d8226ed 378
045fd571
EV
379static const struct comp_type {
380 size_t magic_size;
381 enum kmod_file_compression_type compression;
382 const char *magic_bytes;
383 int (*load)(struct kmod_file *file);
384} comp_types[] = {
385 {sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, load_zstd},
386 {sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, load_xz},
387 {sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, load_zlib},
5a8b16b7 388 {0, KMOD_FILE_COMPRESSION_NONE, NULL, load_reg}
045fd571
EV
389};
390
1eff942e
LDM
391struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
392{
81e5c797
EV
393 int err;
394
1eff942e
LDM
395 if (file->elf)
396 return file->elf;
397
81e5c797
EV
398 err = kmod_file_load_contents(file);
399 if (err) {
400 errno = err;
401 return NULL;
402 }
403
1eff942e
LDM
404 file->elf = kmod_elf_new(file->memory, file->size);
405 return file->elf;
406}
407
c68e92f7
LDM
408struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
409 const char *filename)
3d8226ed 410{
61bf8e74 411 struct kmod_file *file;
61bf8e74
EV
412 char buf[7];
413 ssize_t sz;
414
415 assert_cc(sizeof(magic_zstd) < sizeof(buf));
416 assert_cc(sizeof(magic_xz) < sizeof(buf));
417 assert_cc(sizeof(magic_zlib) < sizeof(buf));
3d8226ed 418
61bf8e74 419 file = calloc(1, sizeof(struct kmod_file));
3d8226ed
GSB
420 if (file == NULL)
421 return NULL;
422
bb417099
GSB
423 file->fd = open(filename, O_RDONLY|O_CLOEXEC);
424 if (file->fd < 0) {
61bf8e74
EV
425 free(file);
426 return NULL;
bb417099
GSB
427 }
428
61bf8e74
EV
429 sz = read_str_safe(file->fd, buf, sizeof(buf));
430 lseek(file->fd, 0, SEEK_SET);
431 if (sz != (sizeof(buf) - 1)) {
432 if (sz < 0)
433 errno = -sz;
434 else
435 errno = EINVAL;
db5d14cf 436
61bf8e74
EV
437 close(file->fd);
438 free(file);
439 return NULL;
440 }
441
5a8b16b7
EV
442 for (unsigned int i = 0; i < ARRAY_SIZE(comp_types); i++) {
443 const struct comp_type *itr = &comp_types[i];
444
445 file->load = itr->load;
446 file->compression = itr->compression;
447 if (itr->magic_size &&
448 memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
61bf8e74 449 break;
db5d14cf 450 }
db5d14cf
GSB
451 }
452
c68e92f7 453 file->ctx = ctx;
7a86f129 454
3d8226ed
GSB
455 return file;
456}
457
7a86f129
LDM
458/*
459 * Callers should just check file->memory got updated
460 */
81e5c797 461int kmod_file_load_contents(struct kmod_file *file)
7a86f129
LDM
462{
463 if (file->memory)
81e5c797 464 return 0;
7a86f129
LDM
465
466 /* The load functions already log possible errors. */
81e5c797 467 return file->load(file);
7a86f129
LDM
468}
469
3d8226ed
GSB
470void *kmod_file_get_contents(const struct kmod_file *file)
471{
472 return file->memory;
473}
474
475off_t kmod_file_get_size(const struct kmod_file *file)
476{
477 return file->size;
478}
479
09c9f8c5 480enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file)
144d1826 481{
09c9f8c5 482 return file->compression;
144d1826
KC
483}
484
485int kmod_file_get_fd(const struct kmod_file *file)
486{
487 return file->fd;
488}
489
3d8226ed
GSB
490void kmod_file_unref(struct kmod_file *file)
491{
1eff942e
LDM
492 if (file->elf)
493 kmod_elf_unref(file->elf);
494
ad158923
EV
495 if (file->compression == KMOD_FILE_COMPRESSION_NONE) {
496 if (file->memory)
497 munmap(file->memory, file->size);
498 } else {
499 free(file->memory);
500 }
7a86f129 501
d6cd6c74 502 close(file->fd);
3d8226ed
GSB
503 free(file);
504}