]> git.ipfire.org Git - thirdparty/kmod.git/blob - libkmod/libkmod-file.c
Reorder and reorganize header files
[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 <errno.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/mman.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #ifdef ENABLE_XZ
31 #include <lzma.h>
32 #endif
33 #ifdef ENABLE_ZLIB
34 #include <zlib.h>
35 #endif
36
37 #include <shared/util.h>
38
39 #include "libkmod.h"
40 #include "libkmod-internal.h"
41
42 struct kmod_file;
43 struct file_ops {
44 int (*load)(struct kmod_file *file);
45 void (*unload)(struct kmod_file *file);
46 };
47
48 struct kmod_file {
49 #ifdef ENABLE_XZ
50 bool xz_used;
51 #endif
52 #ifdef ENABLE_ZLIB
53 gzFile gzf;
54 #endif
55 int fd;
56 bool direct;
57 off_t size;
58 void *memory;
59 const struct file_ops *ops;
60 const struct kmod_ctx *ctx;
61 struct kmod_elf *elf;
62 };
63
64 #ifdef ENABLE_XZ
65 static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
66 {
67 switch (ret) {
68 case LZMA_MEM_ERROR:
69 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
70 break;
71 case LZMA_FORMAT_ERROR:
72 ERR(file->ctx, "xz: File format not recognized\n");
73 break;
74 case LZMA_OPTIONS_ERROR:
75 ERR(file->ctx, "xz: Unsupported compression options\n");
76 break;
77 case LZMA_DATA_ERROR:
78 ERR(file->ctx, "xz: File is corrupt\n");
79 break;
80 case LZMA_BUF_ERROR:
81 ERR(file->ctx, "xz: Unexpected end of input\n");
82 break;
83 default:
84 ERR(file->ctx, "xz: Internal error (bug)\n");
85 break;
86 }
87 }
88
89 static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
90 {
91 uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
92 lzma_action action = LZMA_RUN;
93 lzma_ret ret;
94 void *p = NULL;
95 size_t total = 0;
96
97 strm->avail_in = 0;
98 strm->next_out = out_buf;
99 strm->avail_out = sizeof(out_buf);
100
101 while (true) {
102 if (strm->avail_in == 0) {
103 ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
104 if (rdret < 0) {
105 ret = -errno;
106 goto out;
107 }
108 strm->next_in = in_buf;
109 strm->avail_in = rdret;
110 if (rdret == 0)
111 action = LZMA_FINISH;
112 }
113 ret = lzma_code(strm, action);
114 if (strm->avail_out == 0 || ret != LZMA_OK) {
115 size_t write_size = BUFSIZ - strm->avail_out;
116 char *tmp = realloc(p, total + write_size);
117 if (tmp == NULL) {
118 ret = -errno;
119 goto out;
120 }
121 memcpy(tmp + total, out_buf, write_size);
122 total += write_size;
123 p = tmp;
124 strm->next_out = out_buf;
125 strm->avail_out = BUFSIZ;
126 }
127 if (ret == LZMA_STREAM_END)
128 break;
129 if (ret != LZMA_OK) {
130 xz_uncompress_belch(file, ret);
131 ret = -EINVAL;
132 goto out;
133 }
134 }
135 file->xz_used = true;
136 file->memory = p;
137 file->size = total;
138 return 0;
139 out:
140 free(p);
141 return ret;
142 }
143
144 static int load_xz(struct kmod_file *file)
145 {
146 lzma_stream strm = LZMA_STREAM_INIT;
147 lzma_ret lzret;
148 int ret;
149
150 lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
151 if (lzret == LZMA_MEM_ERROR) {
152 ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
153 return -ENOMEM;
154 } else if (lzret != LZMA_OK) {
155 ERR(file->ctx, "xz: Internal error (bug)\n");
156 return -EINVAL;
157 }
158 ret = xz_uncompress(&strm, file);
159 lzma_end(&strm);
160 return ret;
161 }
162
163 static void unload_xz(struct kmod_file *file)
164 {
165 if (!file->xz_used)
166 return;
167 free(file->memory);
168 }
169
170 static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
171 #endif
172
173 #ifdef ENABLE_ZLIB
174 #define READ_STEP (4 * 1024 * 1024)
175 static int load_zlib(struct kmod_file *file)
176 {
177 int err = 0;
178 off_t did = 0, total = 0;
179 _cleanup_free_ unsigned char *p = NULL;
180
181 errno = 0;
182 file->gzf = gzdopen(file->fd, "rb");
183 if (file->gzf == NULL)
184 return -errno;
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 p = NULL;
219 return 0;
220
221 error:
222 gzclose(file->gzf);
223 return err;
224 }
225
226 static void unload_zlib(struct kmod_file *file)
227 {
228 if (file->gzf == NULL)
229 return;
230 free(file->memory);
231 gzclose(file->gzf); /* closes file->fd */
232 }
233
234 static const char magic_zlib[] = {0x1f, 0x8b};
235 #endif
236
237 static const struct comp_type {
238 size_t magic_size;
239 const char *magic_bytes;
240 const struct file_ops ops;
241 } comp_types[] = {
242 #ifdef ENABLE_XZ
243 {sizeof(magic_xz), magic_xz, {load_xz, unload_xz}},
244 #endif
245 #ifdef ENABLE_ZLIB
246 {sizeof(magic_zlib), magic_zlib, {load_zlib, unload_zlib}},
247 #endif
248 {0, NULL, {NULL, NULL}}
249 };
250
251 static int load_reg(struct kmod_file *file)
252 {
253 struct stat st;
254
255 if (fstat(file->fd, &st) < 0)
256 return -errno;
257
258 file->size = st.st_size;
259 file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
260 file->fd, 0);
261 if (file->memory == MAP_FAILED)
262 return -errno;
263 file->direct = true;
264 return 0;
265 }
266
267 static void unload_reg(struct kmod_file *file)
268 {
269 munmap(file->memory, file->size);
270 }
271
272 static const struct file_ops reg_ops = {
273 load_reg, unload_reg
274 };
275
276 struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
277 {
278 if (file->elf)
279 return file->elf;
280
281 file->elf = kmod_elf_new(file->memory, file->size);
282 return file->elf;
283 }
284
285 struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
286 const char *filename)
287 {
288 struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
289 const struct comp_type *itr;
290 size_t magic_size_max = 0;
291 int err;
292
293 if (file == NULL)
294 return NULL;
295
296 file->fd = open(filename, O_RDONLY|O_CLOEXEC);
297 if (file->fd < 0) {
298 err = -errno;
299 goto error;
300 }
301
302 for (itr = comp_types; itr->ops.load != NULL; itr++) {
303 if (magic_size_max < itr->magic_size)
304 magic_size_max = itr->magic_size;
305 }
306
307 file->direct = false;
308 if (magic_size_max > 0) {
309 char *buf = alloca(magic_size_max + 1);
310 ssize_t sz;
311
312 if (buf == NULL) {
313 err = -errno;
314 goto error;
315 }
316 sz = read_str_safe(file->fd, buf, magic_size_max + 1);
317 lseek(file->fd, 0, SEEK_SET);
318 if (sz != (ssize_t)magic_size_max) {
319 if (sz < 0)
320 err = sz;
321 else
322 err = -EINVAL;
323 goto error;
324 }
325
326 for (itr = comp_types; itr->ops.load != NULL; itr++) {
327 if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0)
328 break;
329 }
330 if (itr->ops.load != NULL)
331 file->ops = &itr->ops;
332 }
333
334 if (file->ops == NULL)
335 file->ops = &reg_ops;
336
337 err = file->ops->load(file);
338 file->ctx = ctx;
339 error:
340 if (err < 0) {
341 if (file->fd >= 0)
342 close(file->fd);
343 free(file);
344 errno = -err;
345 return NULL;
346 }
347
348 return file;
349 }
350
351 void *kmod_file_get_contents(const struct kmod_file *file)
352 {
353 return file->memory;
354 }
355
356 off_t kmod_file_get_size(const struct kmod_file *file)
357 {
358 return file->size;
359 }
360
361 bool kmod_file_get_direct(const struct kmod_file *file)
362 {
363 return file->direct;
364 }
365
366 int kmod_file_get_fd(const struct kmod_file *file)
367 {
368 return file->fd;
369 }
370
371 void kmod_file_unref(struct kmod_file *file)
372 {
373 if (file->elf)
374 kmod_elf_unref(file->elf);
375
376 file->ops->unload(file);
377 if (file->fd >= 0)
378 close(file->fd);
379 free(file);
380 }