]> git.ipfire.org Git - thirdparty/kmod.git/blob - libkmod/libkmod-file.c
libkmod: add finit_module logic
[thirdparty/kmod.git] / libkmod / libkmod-file.c
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, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <stdbool.h>
22 #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
34 #ifdef ENABLE_XZ
35 #include <lzma.h>
36 #endif
37 #ifdef ENABLE_ZLIB
38 #include <zlib.h>
39 #endif
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
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
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
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
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)
174 static int load_zlib(struct kmod_file *file)
175 {
176 int err = 0;
177 off_t did = 0, total = 0;
178 unsigned char *p = NULL;
179
180 errno = 0;
181 file->gzf = gzdopen(file->fd, "rb");
182 if (file->gzf == NULL) {
183 return -errno;
184 }
185 file->fd = -1; /* now owned by gzf due gzdopen() */
186
187 for (;;) {
188 int r;
189
190 if (did == total) {
191 void *tmp = realloc(p, total + READ_STEP);
192 if (tmp == NULL) {
193 err = -errno;
194 goto error;
195 }
196 total += READ_STEP;
197 p = tmp;
198 }
199
200 r = gzread(file->gzf, p + did, total - did);
201 if (r == 0)
202 break;
203 else if (r < 0) {
204 int gzerr;
205 const char *gz_errmsg = gzerror(file->gzf, &gzerr);
206
207 ERR(file->ctx, "gzip: %s\n", gz_errmsg);
208
209 /* gzip might not set errno here */
210 err = gzerr == Z_ERRNO ? -errno : -EINVAL;
211 goto error;
212 }
213 did += r;
214 }
215
216 file->memory = p;
217 file->size = did;
218 return 0;
219 error:
220 free(p);
221 gzclose(file->gzf);
222 return err;
223 }
224
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
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(0, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
259 if (file->memory == MAP_FAILED)
260 return -errno;
261 file->direct = true;
262 return 0;
263 }
264
265 static void unload_reg(struct kmod_file *file)
266 {
267 munmap(file->memory, file->size);
268 }
269
270 static const struct file_ops reg_ops = {
271 load_reg, unload_reg
272 };
273
274 struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
275 {
276 if (file->elf)
277 return file->elf;
278
279 file->elf = kmod_elf_new(file->memory, file->size);
280 return file->elf;
281 }
282
283 struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
284 const char *filename)
285 {
286 struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
287 const struct comp_type *itr;
288 size_t magic_size_max = 0;
289 int err;
290
291 if (file == NULL)
292 return NULL;
293
294 file->fd = open(filename, O_RDONLY|O_CLOEXEC);
295 if (file->fd < 0) {
296 err = -errno;
297 goto error;
298 }
299
300 for (itr = comp_types; itr->ops.load != NULL; itr++) {
301 if (magic_size_max < itr->magic_size)
302 magic_size_max = itr->magic_size;
303 }
304
305 file->direct = false;
306 if (magic_size_max > 0) {
307 char *buf = alloca(magic_size_max + 1);
308 ssize_t sz;
309
310 if (buf == NULL) {
311 err = -errno;
312 goto error;
313 }
314 sz = read_str_safe(file->fd, buf, magic_size_max + 1);
315 lseek(file->fd, 0, SEEK_SET);
316 if (sz != (ssize_t)magic_size_max) {
317 if (sz < 0)
318 err = sz;
319 else
320 err = -EINVAL;
321 goto error;
322 }
323
324 for (itr = comp_types; itr->ops.load != NULL; itr++) {
325 if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0)
326 break;
327 }
328 if (itr->ops.load != NULL)
329 file->ops = &itr->ops;
330 }
331
332 if (file->ops == NULL)
333 file->ops = &reg_ops;
334
335 err = file->ops->load(file);
336 file->ctx = ctx;
337 error:
338 if (err < 0) {
339 if (file->fd >= 0)
340 close(file->fd);
341 free(file);
342 errno = -err;
343 return NULL;
344 }
345
346 return file;
347 }
348
349 void *kmod_file_get_contents(const struct kmod_file *file)
350 {
351 return file->memory;
352 }
353
354 off_t kmod_file_get_size(const struct kmod_file *file)
355 {
356 return file->size;
357 }
358
359 bool kmod_file_get_direct(const struct kmod_file *file)
360 {
361 return file->direct;
362 }
363
364 int kmod_file_get_fd(const struct kmod_file *file)
365 {
366 return file->fd;
367 }
368
369 void kmod_file_unref(struct kmod_file *file)
370 {
371 if (file->elf)
372 kmod_elf_unref(file->elf);
373
374 file->ops->unload(file);
375 if (file->fd >= 0)
376 close(file->fd);
377 free(file);
378 }