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 = ®_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 } 380