gitignore: ignore release files
[platform/upstream/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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <errno.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #ifdef ENABLE_XZ
30 #include <lzma.h>
31 #endif
32 #ifdef ENABLE_ZLIB
33 #include <zlib.h>
34 #endif
35
36 #include <shared/util.h>
37
38 #include "libkmod.h"
39 #include "libkmod-internal.h"
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         _cleanup_free_ unsigned char *p = NULL;
179
180         errno = 0;
181         file->gzf = gzdopen(file->fd, "rb");
182         if (file->gzf == NULL)
183                 return -errno;
184         file->fd = -1; /* now owned by gzf due gzdopen() */
185
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) {
203                         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;
210                         goto error;
211                 }
212                 did += r;
213         }
214
215         file->memory = p;
216         file->size = did;
217         p = NULL;
218         return 0;
219
220 error:
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(NULL, file->size, PROT_READ, MAP_PRIVATE,
259                             file->fd, 0);
260         if (file->memory == MAP_FAILED)
261                 return -errno;
262         file->direct = true;
263         return 0;
264 }
265
266 static void unload_reg(struct kmod_file *file)
267 {
268         munmap(file->memory, file->size);
269 }
270
271 static const struct file_ops reg_ops = {
272         load_reg, unload_reg
273 };
274
275 struct 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
284 struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
285                                                 const char *filename)
286 {
287         struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
288         const struct comp_type *itr;
289         size_t magic_size_max = 0;
290         int err;
291
292         if (file == NULL)
293                 return NULL;
294
295         file->fd = open(filename, O_RDONLY|O_CLOEXEC);
296         if (file->fd < 0) {
297                 err = -errno;
298                 goto error;
299         }
300
301         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         }
305
306         file->direct = false;
307         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);
337         file->ctx = ctx;
338 error:
339         if (err < 0) {
340                 if (file->fd >= 0)
341                         close(file->fd);
342                 free(file);
343                 errno = -err;
344                 return NULL;
345         }
346
347         return file;
348 }
349
350 void *kmod_file_get_contents(const struct kmod_file *file)
351 {
352         return file->memory;
353 }
354
355 off_t kmod_file_get_size(const struct kmod_file *file)
356 {
357         return file->size;
358 }
359
360 bool kmod_file_get_direct(const struct kmod_file *file)
361 {
362         return file->direct;
363 }
364
365 int kmod_file_get_fd(const struct kmod_file *file)
366 {
367         return file->fd;
368 }
369
370 void kmod_file_unref(struct kmod_file *file)
371 {
372         if (file->elf)
373                 kmod_elf_unref(file->elf);
374
375         file->ops->unload(file);
376         if (file->fd >= 0)
377                 close(file->fd);
378         free(file);
379 }