blob: ced20a8f4fa4eaa205324d0391fd83173f583b9a [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
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
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>
24#include <errno.h>
25#include <string.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <sys/mman.h>
29#include <unistd.h>
30
31#include "libkmod.h"
32#include "libkmod-private.h"
33
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010034#ifdef ENABLE_XZ
35#include <lzma.h>
36#endif
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020037#ifdef ENABLE_ZLIB
38#include <zlib.h>
39#endif
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;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020055 off_t size;
56 void *memory;
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -020057 const struct file_ops *ops;
Lucas De Marchic68e92f2012-01-04 08:19:34 -020058 const struct kmod_ctx *ctx;
Lucas De Marchi1eff9422012-10-18 01:36:33 -030059 struct kmod_elf *elf;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -020060};
61
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010062#ifdef ENABLE_XZ
Lucas De Marchi3f635052012-01-04 08:23:15 -020063static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010064{
65 switch (ret) {
66 case LZMA_MEM_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020067 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010068 break;
69 case LZMA_FORMAT_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020070 ERR(file->ctx, "xz: File format not recognized\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010071 break;
72 case LZMA_OPTIONS_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020073 ERR(file->ctx, "xz: Unsupported compression options\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010074 break;
75 case LZMA_DATA_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020076 ERR(file->ctx, "xz: File is corrupt\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010077 break;
78 case LZMA_BUF_ERROR:
Lucas De Marchi3f635052012-01-04 08:23:15 -020079 ERR(file->ctx, "xz: Unexpected end of input\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010080 break;
81 default:
Lucas De Marchi3f635052012-01-04 08:23:15 -020082 ERR(file->ctx, "xz: Internal error (bug)\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +010083 break;
84 }
85}
86
87static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
88{
89 uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
90 lzma_action action = LZMA_RUN;
91 lzma_ret ret;
92 void *p = NULL;
93 size_t total = 0;
94
95 strm->avail_in = 0;
96 strm->next_out = out_buf;
97 strm->avail_out = sizeof(out_buf);
98
99 while (true) {
100 if (strm->avail_in == 0) {
101 ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
102 if (rdret < 0) {
103 ret = -errno;
104 goto out;
105 }
106 strm->next_in = in_buf;
107 strm->avail_in = rdret;
108 if (rdret == 0)
109 action = LZMA_FINISH;
110 }
111 ret = lzma_code(strm, action);
112 if (strm->avail_out == 0 || ret != LZMA_OK) {
113 size_t write_size = BUFSIZ - strm->avail_out;
114 char *tmp = realloc(p, total + write_size);
115 if (tmp == NULL) {
116 ret = -errno;
117 goto out;
118 }
119 memcpy(tmp + total, out_buf, write_size);
120 total += write_size;
121 p = tmp;
122 strm->next_out = out_buf;
123 strm->avail_out = BUFSIZ;
124 }
125 if (ret == LZMA_STREAM_END)
126 break;
127 if (ret != LZMA_OK) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200128 xz_uncompress_belch(file, ret);
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100129 ret = -EINVAL;
130 goto out;
131 }
132 }
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200133 file->xz_used = true;
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100134 file->memory = p;
135 file->size = total;
136 return 0;
137 out:
138 free(p);
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100139 return ret;
140}
141
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200142static int load_xz(struct kmod_file *file)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100143{
144 lzma_stream strm = LZMA_STREAM_INIT;
145 lzma_ret lzret;
146 int ret;
147
148 lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
149 if (lzret == LZMA_MEM_ERROR) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200150 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100151 return -ENOMEM;
152 } else if (lzret != LZMA_OK) {
Lucas De Marchi3f635052012-01-04 08:23:15 -0200153 ERR(file->ctx, "xz: Internal error (bug)\n");
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100154 return -EINVAL;
155 }
156 ret = xz_uncompress(&strm, file);
157 lzma_end(&strm);
158 return ret;
159}
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200160
161static void unload_xz(struct kmod_file *file)
162{
163 if (!file->xz_used)
164 return;
165 free(file->memory);
166}
167
168static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100169#endif
170
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200171#ifdef ENABLE_ZLIB
172#define READ_STEP (4 * 1024 * 1024)
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200173static int load_zlib(struct kmod_file *file)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200174{
175 int err = 0;
176 off_t did = 0, total = 0;
177 unsigned char *p = NULL;
178
179 errno = 0;
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200180 file->gzf = gzdopen(file->fd, "rb");
181 if (file->gzf == NULL) {
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200182 return -errno;
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200183 }
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;
217 return 0;
218error:
219 free(p);
220 gzclose(file->gzf);
221 return err;
222}
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200223
224static void unload_zlib(struct kmod_file *file)
225{
226 if (file->gzf == NULL)
227 return;
228 free(file->memory);
229 gzclose(file->gzf); /* closes file->fd */
230}
231
232static const char magic_zlib[] = {0x1f, 0x8b};
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200233#endif
234
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200235static const struct comp_type {
236 size_t magic_size;
237 const char *magic_bytes;
238 const struct file_ops ops;
239} comp_types[] = {
240#ifdef ENABLE_XZ
241 {sizeof(magic_xz), magic_xz, {load_xz, unload_xz}},
242#endif
243#ifdef ENABLE_ZLIB
244 {sizeof(magic_zlib), magic_zlib, {load_zlib, unload_zlib}},
245#endif
246 {0, NULL, {NULL, NULL}}
247};
248
249static int load_reg(struct kmod_file *file)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200250{
251 struct stat st;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200252
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200253 if (fstat(file->fd, &st) < 0)
254 return -errno;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200255
256 file->size = st.st_size;
257 file->memory = mmap(0, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200258 if (file->memory == MAP_FAILED)
259 return -errno;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200260 return 0;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200261}
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200262
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200263static void unload_reg(struct kmod_file *file)
264{
265 munmap(file->memory, file->size);
266}
267
Lucas De Marchi7749bed2012-01-04 08:00:45 -0200268static const struct file_ops reg_ops = {
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200269 load_reg, unload_reg
270};
271
Lucas De Marchi1eff9422012-10-18 01:36:33 -0300272struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
273{
274 if (file->elf)
275 return file->elf;
276
277 file->elf = kmod_elf_new(file->memory, file->size);
278 return file->elf;
279}
280
Lucas De Marchic68e92f2012-01-04 08:19:34 -0200281struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
282 const char *filename)
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200283{
284 struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200285 const struct comp_type *itr;
286 size_t magic_size_max = 0;
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200287 int err;
288
289 if (file == NULL)
290 return NULL;
291
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200292 file->fd = open(filename, O_RDONLY|O_CLOEXEC);
293 if (file->fd < 0) {
294 err = -errno;
295 goto error;
296 }
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200297
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200298 for (itr = comp_types; itr->ops.load != NULL; itr++) {
299 if (magic_size_max < itr->magic_size)
300 magic_size_max = itr->magic_size;
301 }
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200302
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200303 if (magic_size_max > 0) {
304 char *buf = alloca(magic_size_max + 1);
305 ssize_t sz;
306
307 if (buf == NULL) {
308 err = -errno;
309 goto error;
310 }
311 sz = read_str_safe(file->fd, buf, magic_size_max + 1);
312 lseek(file->fd, 0, SEEK_SET);
313 if (sz != (ssize_t)magic_size_max) {
314 if (sz < 0)
315 err = sz;
316 else
317 err = -EINVAL;
318 goto error;
319 }
320
321 for (itr = comp_types; itr->ops.load != NULL; itr++) {
322 if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0)
323 break;
324 }
325 if (itr->ops.load != NULL)
326 file->ops = &itr->ops;
327 }
328
329 if (file->ops == NULL)
330 file->ops = &reg_ops;
331
332 err = file->ops->load(file);
Lucas De Marchic68e92f2012-01-04 08:19:34 -0200333 file->ctx = ctx;
Gustavo Sverzut Barbieribb417092011-12-24 00:09:31 -0200334error:
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200335 if (err < 0) {
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200336 if (file->fd >= 0)
337 close(file->fd);
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200338 free(file);
339 errno = -err;
340 return NULL;
341 }
342
343 return file;
344}
345
346void *kmod_file_get_contents(const struct kmod_file *file)
347{
348 return file->memory;
349}
350
351off_t kmod_file_get_size(const struct kmod_file *file)
352{
353 return file->size;
354}
355
356void kmod_file_unref(struct kmod_file *file)
357{
Lucas De Marchi1eff9422012-10-18 01:36:33 -0300358 if (file->elf)
359 kmod_elf_unref(file->elf);
360
Gustavo Sverzut Barbieridb5d14c2012-01-03 14:25:49 -0200361 file->ops->unload(file);
362 if (file->fd >= 0)
Jan Engelhardtb182f8f2011-12-24 14:58:30 +0100363 close(file->fd);
Gustavo Sverzut Barbieri3d8226e2011-12-16 16:08:53 -0200364 free(file);
365}