blob: 5eeba6a912a2ff987ec3e471da83b92d158182d3 [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>
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010029#ifdef ENABLE_XZ
30#include <lzma.h>
31#endif
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020032#ifdef ENABLE_ZLIB
33#include <zlib.h>
34#endif
35
Lucas De Marchic2e42862014-10-03 01:41:42 -030036#include <shared/util.h>
37
38#include "libkmod.h"
39#include "libkmod-internal.h"
40
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -020041struct kmod_file;
42struct file_ops {
43 int (*load)(struct kmod_file *file);
44 void (*unload)(struct kmod_file *file);
45};
46
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020047struct kmod_file {
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010048#ifdef ENABLE_XZ
49 bool xz_used;
50#endif
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020051#ifdef ENABLE_ZLIB
52 gzFile gzf;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020053#endif
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -020054 int fd;
Kees Cook144d1822013-02-18 12:02:32 -080055 bool direct;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020056 off_t size;
57 void *memory;
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -020058 const struct file_ops *ops;
Lucas De Marchic68e92f2012-01-04 08:19:34 -020059 const struct kmod_ctx *ctx;
Lucas De Marchi1eff9422012-10-18 01:36:33 -030060 struct kmod_elf *elf;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020061};
62
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010063#ifdef ENABLE_XZ
Lucas De Marchi3f635052012-01-04 08:23:15 -020064static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010065{
66 switch (ret) {
67 case LZMA_MEM_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020068 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010069 break;
70 case LZMA_FORMAT_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020071 ERR(file->ctx, "xz: File format not recognized\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010072 break;
73 case LZMA_OPTIONS_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020074 ERR(file->ctx, "xz: Unsupported compression options\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010075 break;
76 case LZMA_DATA_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020077 ERR(file->ctx, "xz: File is corrupt\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010078 break;
79 case LZMA_BUF_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020080 ERR(file->ctx, "xz: Unexpected end of input\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010081 break;
82 default:
Lucas De Marchi3f635052012-01-04 08:23:15 -020083 ERR(file->ctx, "xz: Internal error (bug)\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010084 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) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200129 xz_uncompress_belch(file, ret);
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100130 ret = -EINVAL;
131 goto out;
132 }
133 }
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200134 file->xz_used = true;
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100135 file->memory = p;
136 file->size = total;
137 return 0;
138 out:
139 free(p);
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100140 return ret;
141}
142
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200143static int load_xz(struct kmod_file *file)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100144{
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) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200151 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100152 return -ENOMEM;
153 } else if (lzret != LZMA_OK) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200154 ERR(file->ctx, "xz: Internal error (bug)\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100155 return -EINVAL;
156 }
157 ret = xz_uncompress(&strm, file);
158 lzma_end(&strm);
159 return ret;
160}
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200161
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};
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100170#endif
171
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200172#ifdef ENABLE_ZLIB
173#define READ_STEP (4 * 1024 * 1024)
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200174static int load_zlib(struct kmod_file *file)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200175{
176 int err = 0;
177 off_t did = 0, total = 0;
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200178 _cleanup_free_ unsigned char *p = NULL;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200179
180 errno = 0;
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200181 file->gzf = gzdopen(file->fd, "rb");
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200182 if (file->gzf == NULL)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200183 return -errno;
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200184 file->fd = -1; /* now owned by gzf due gzdopen() */
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200185
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) {
Dave Reisnerc7d5a602012-05-07 19:41:41 -0400203 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;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200210 goto error;
211 }
212 did += r;
213 }
214
215 file->memory = p;
216 file->size = did;
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200217 p = NULL;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200218 return 0;
Lucas De Marchid3c16c72013-11-18 11:43:10 -0200219
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200220error:
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200221 gzclose(file->gzf);
222 return err;
223}
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200224
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};
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200234#endif
235
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200236static 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)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200251{
252 struct stat st;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200253
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200254 if (fstat(file->fd, &st) < 0)
255 return -errno;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200256
257 file->size = st.st_size;
Kees Cookc3e8d262013-02-18 12:02:34 -0800258 file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
259 file->fd, 0);
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200260 if (file->memory == MAP_FAILED)
261 return -errno;
Kees Cook144d1822013-02-18 12:02:32 -0800262 file->direct = true;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200263 return 0;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200264}
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200265
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200266static void unload_reg(struct kmod_file *file)
267{
268 munmap(file->memory, file->size);
269}
270
Lucas De Marchi7749bed2012-01-04 08:00:45 -0200271static const struct file_ops reg_ops = {
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200272 load_reg, unload_reg
273};
274
Lucas De Marchi1eff9422012-10-18 01:36:33 -0300275struct 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
Lucas De Marchic68e92f2012-01-04 08:19:34 -0200284struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
285 const char *filename)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200286{
287 struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200288 const struct comp_type *itr;
289 size_t magic_size_max = 0;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200290 int err;
291
292 if (file == NULL)
293 return NULL;
294
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200295 file->fd = open(filename, O_RDONLY|O_CLOEXEC);
296 if (file->fd < 0) {
297 err = -errno;
298 goto error;
299 }
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200300
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200301 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 }
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200305
Kees Cook144d1822013-02-18 12:02:32 -0800306 file->direct = false;
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200307 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);
Lucas De Marchic68e92f2012-01-04 08:19:34 -0200337 file->ctx = ctx;
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200338error:
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200339 if (err < 0) {
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200340 if (file->fd >= 0)
341 close(file->fd);
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200342 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
Kees Cook144d1822013-02-18 12:02:32 -0800360bool 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
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200370void kmod_file_unref(struct kmod_file *file)
371{
Lucas De Marchi1eff9422012-10-18 01:36:33 -0300372 if (file->elf)
373 kmod_elf_unref(file->elf);
374
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200375 file->ops->unload(file);
376 if (file->fd >= 0)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100377 close(file->fd);
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200378 free(file);
379}