1 /* 2 * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 6 * as published by the Free Software Foundation 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 #include <sys/types.h> 14 #include <stdio.h> 15 #include <getopt.h> 16 #include <stdbool.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #include "fwimage.h" 22 #include "utils.h" 23 #include "crc32.h" 24 25 #define METADATA_MAXLEN 30 * 1024 26 #define SIGNATURE_MAXLEN 1 * 1024 27 28 #define BUFLEN (METADATA_MAXLEN + SIGNATURE_MAXLEN + 1024) 29 30 enum { 31 MODE_DEFAULT = -1, 32 MODE_EXTRACT = 0, 33 MODE_APPEND = 1, 34 }; 35 36 struct data_buf { 37 char *cur; 38 char *prev; 39 int cur_len; 40 int file_len; 41 }; 42 43 static FILE *signature_file, *metadata_file, *firmware_file; 44 static int file_mode = MODE_DEFAULT; 45 static bool truncate_file; 46 static bool write_truncated; 47 static bool quiet = false; 48 49 static uint32_t crc_table[256]; 50 51 #define msg(...) \ 52 do { \ 53 if (!quiet) \ 54 fprintf(stderr, __VA_ARGS__); \ 55 } while (0) 56 57 static int 58 usage(const char *progname) 59 { 60 fprintf(stderr, "Usage: %s <options> <firmware>\n" 61 "\n" 62 "Options:\n" 63 " -S <file>: Append signature file to firmware image\n" 64 " -I <file>: Append metadata file to firmware image\n" 65 " -s <file>: Extract signature file from firmware image\n" 66 " -i <file>: Extract metadata file from firmware image\n" 67 " -t: Remove extracted chunks from firmare image (using -s, -i)\n" 68 " -T: Output firmware image without extracted chunks to stdout (using -s, -i)\n" 69 " -q: Quiet (suppress error messages)\n" 70 "\n", progname); 71 return 1; 72 } 73 74 static FILE * 75 open_file(const char *name, bool write) 76 { 77 FILE *ret; 78 79 if (!strcmp(name, "-")) 80 return write ? stdout : stdin; 81 82 ret = fopen(name, write ? "w" : "r+"); 83 if (!ret && !write) 84 ret = fopen(name, "r"); 85 86 return ret; 87 } 88 89 static int 90 set_file(FILE **file, const char *name, int mode) 91 { 92 if (file_mode < 0) 93 file_mode = mode; 94 else if (file_mode != mode) { 95 msg("Error: mixing appending and extracting data is not supported\n"); 96 return 1; 97 } 98 99 if (*file) { 100 msg("Error: the same append/extract option cannot be used multiple times\n"); 101 return 1; 102 } 103 104 *file = open_file(name, mode == MODE_EXTRACT); 105 return !*file; 106 } 107 108 static void 109 trailer_update_crc(struct fwimage_trailer *tr, void *buf, int len) 110 { 111 tr->crc32 = cpu_to_be32(crc32_block(be32_to_cpu(tr->crc32), buf, len, crc_table)); 112 } 113 114 static int 115 append_data(FILE *in, FILE *out, struct fwimage_trailer *tr, int maxlen) 116 { 117 while (1) { 118 char buf[512]; 119 int len; 120 121 len = fread(buf, 1, sizeof(buf), in); 122 if (!len) 123 break; 124 125 maxlen -= len; 126 if (maxlen < 0) 127 return 1; 128 129 tr->size += len; 130 trailer_update_crc(tr, buf, len); 131 fwrite(buf, len, 1, out); 132 } 133 134 return 0; 135 } 136 137 static void 138 append_trailer(FILE *out, struct fwimage_trailer *tr) 139 { 140 tr->size = cpu_to_be32(tr->size); 141 fwrite(tr, sizeof(*tr), 1, out); 142 trailer_update_crc(tr, tr, sizeof(*tr)); 143 } 144 145 static int 146 add_metadata(struct fwimage_trailer *tr) 147 { 148 struct fwimage_header hdr; 149 150 tr->type = FWIMAGE_INFO; 151 tr->size = sizeof(hdr) + sizeof(*tr); 152 153 memset(&hdr, 0, sizeof(hdr)); 154 trailer_update_crc(tr, &hdr, sizeof(hdr)); 155 fwrite(&hdr, sizeof(hdr), 1, firmware_file); 156 157 if (append_data(metadata_file, firmware_file, tr, METADATA_MAXLEN)) 158 return 1; 159 160 append_trailer(firmware_file, tr); 161 162 return 0; 163 } 164 165 static int 166 add_signature(struct fwimage_trailer *tr) 167 { 168 if (!signature_file) 169 return 0; 170 171 tr->type = FWIMAGE_SIGNATURE; 172 tr->size = sizeof(*tr); 173 174 if (append_data(signature_file, firmware_file, tr, SIGNATURE_MAXLEN)) 175 return 1; 176 177 append_trailer(firmware_file, tr); 178 179 return 0; 180 } 181 182 static int 183 add_data(const char *name) 184 { 185 struct fwimage_trailer tr; 186 int file_len = 0; 187 int ret = 0; 188 189 memset(&tr, 0, sizeof(tr)); 190 191 tr.crc32 = ~0; 192 tr.magic = cpu_to_be32(FWIMAGE_MAGIC); 193 194 firmware_file = fopen(name, "r+"); 195 if (!firmware_file) { 196 msg("Failed to open firmware file\n"); 197 return 1; 198 } 199 200 while (1) { 201 char buf[512]; 202 int len; 203 204 len = fread(buf, 1, sizeof(buf), firmware_file); 205 if (!len) 206 break; 207 208 file_len += len; 209 trailer_update_crc(&tr, buf, len); 210 } 211 212 if (metadata_file) 213 ret = add_metadata(&tr); 214 else if (signature_file) 215 ret = add_signature(&tr); 216 217 if (ret) { 218 fflush(firmware_file); 219 ret = ftruncate(fileno(firmware_file), file_len); 220 if (ret < 0) 221 msg("Error during ftruncate: %m\n"); 222 } 223 224 return ret; 225 } 226 227 static void 228 remove_tail(struct data_buf *dbuf, int len) 229 { 230 dbuf->cur_len -= len; 231 dbuf->file_len -= len; 232 233 if (dbuf->cur_len) 234 return; 235 236 free(dbuf->cur); 237 dbuf->cur = dbuf->prev; 238 dbuf->prev = NULL; 239 dbuf->cur_len = BUFLEN; 240 } 241 242 static int 243 extract_tail(struct data_buf *dbuf, void *dest, int len) 244 { 245 int cur_len = dbuf->cur_len; 246 247 if (!dbuf->cur) 248 return 1; 249 250 if (cur_len >= len) 251 cur_len = len; 252 253 memcpy(dest + (len - cur_len), dbuf->cur + dbuf->cur_len - cur_len, cur_len); 254 remove_tail(dbuf, cur_len); 255 256 cur_len = len - cur_len; 257 if (cur_len < 0 || !dbuf->cur) 258 return 1; 259 260 memcpy(dest, dbuf->cur + dbuf->cur_len - cur_len, cur_len); 261 remove_tail(dbuf, cur_len); 262 263 return 0; 264 } 265 266 static uint32_t 267 tail_crc32(struct data_buf *dbuf, uint32_t crc32) 268 { 269 if (dbuf->prev) 270 crc32 = crc32_block(crc32, dbuf->prev, BUFLEN, crc_table); 271 272 return crc32_block(crc32, dbuf->cur, dbuf->cur_len, crc_table); 273 } 274 275 static int 276 validate_metadata(struct fwimage_header *hdr, int data_len) 277 { 278 if (hdr->version != 0) 279 return 1; 280 return 0; 281 } 282 283 static int 284 extract_data(const char *name) 285 { 286 struct fwimage_header *hdr; 287 struct fwimage_trailer tr; 288 struct data_buf dbuf = {}; 289 uint32_t crc32 = ~0; 290 int data_len = 0; 291 int ret = 1; 292 void *buf; 293 bool metadata_keep = false; 294 295 memset(&tr, 0, sizeof(tr)); 296 297 firmware_file = open_file(name, false); 298 if (!firmware_file) { 299 msg("Failed to open firmware file\n"); 300 return 1; 301 } 302 303 if (truncate_file && firmware_file == stdin) { 304 msg("Cannot truncate file when reading from stdin\n"); 305 return 1; 306 } 307 308 buf = malloc(BUFLEN); 309 if (!buf) 310 return 1; 311 312 do { 313 char *tmp = dbuf.cur; 314 315 if (write_truncated && dbuf.prev) 316 fwrite(dbuf.prev, 1, BUFLEN, stdout); 317 318 dbuf.cur = dbuf.prev; 319 dbuf.prev = tmp; 320 321 if (dbuf.cur) 322 crc32 = crc32_block(crc32, dbuf.cur, BUFLEN, crc_table); 323 else 324 dbuf.cur = malloc(BUFLEN); 325 326 if (!dbuf.cur) 327 goto out; 328 329 dbuf.cur_len = fread(dbuf.cur, 1, BUFLEN, firmware_file); 330 dbuf.file_len += dbuf.cur_len; 331 } while (dbuf.cur_len == BUFLEN); 332 333 while (1) { 334 335 if (extract_tail(&dbuf, &tr, sizeof(tr))) { 336 msg("unable to extract trailer header\n"); 337 break; 338 } 339 340 if (tr.magic != cpu_to_be32(FWIMAGE_MAGIC)) { 341 msg("Data not found\n"); 342 metadata_keep = true; 343 break; 344 } 345 346 data_len = be32_to_cpu(tr.size) - sizeof(tr); 347 348 if (be32_to_cpu(tr.crc32) != tail_crc32(&dbuf, crc32)) { 349 msg("CRC error\n"); 350 break; 351 } 352 353 if (data_len > BUFLEN) { 354 msg("Size error\n"); 355 break; 356 } 357 358 if (extract_tail(&dbuf, buf, data_len)) { 359 msg("unable to extract trailer data\n"); 360 break; 361 } 362 363 if (tr.type == FWIMAGE_SIGNATURE) { 364 if (!signature_file) 365 continue; 366 fwrite(buf, data_len, 1, signature_file); 367 ret = 0; 368 break; 369 } else if (tr.type == FWIMAGE_INFO) { 370 if (!metadata_file) { 371 dbuf.file_len += data_len + sizeof(tr); 372 metadata_keep = true; 373 break; 374 } 375 376 hdr = buf; 377 data_len -= sizeof(*hdr); 378 if (validate_metadata(hdr, data_len)) 379 continue; 380 381 fwrite(hdr + 1, data_len, 1, metadata_file); 382 ret = 0; 383 break; 384 } else { 385 continue; 386 } 387 } 388 389 if (!ret && truncate_file) { 390 ret = ftruncate(fileno(firmware_file), dbuf.file_len); 391 if (ret < 0) { 392 msg("Error during ftruncate: %m\n"); 393 goto out; 394 } 395 } 396 397 if (write_truncated) { 398 if (dbuf.prev) 399 fwrite(dbuf.prev, 1, BUFLEN, stdout); 400 if (dbuf.cur) 401 fwrite(dbuf.cur, 1, dbuf.cur_len, stdout); 402 if (metadata_keep) { 403 fwrite(buf, data_len, 1, stdout); 404 fwrite(&tr, sizeof(tr), 1, stdout); 405 } 406 } 407 408 out: 409 free(buf); 410 free(dbuf.cur); 411 free(dbuf.prev); 412 return ret; 413 } 414 415 static void cleanup(void) 416 { 417 if (signature_file) 418 fclose(signature_file); 419 if (metadata_file) 420 fclose(metadata_file); 421 if (firmware_file) 422 fclose(firmware_file); 423 } 424 425 int main(int argc, char **argv) 426 { 427 const char *progname = argv[0]; 428 int ret, ch; 429 430 crc32_filltable(crc_table); 431 432 while ((ch = getopt(argc, argv, "i:I:qs:S:tT")) != -1) { 433 ret = 0; 434 switch(ch) { 435 case 'S': 436 ret = set_file(&signature_file, optarg, MODE_APPEND); 437 break; 438 case 'I': 439 ret = set_file(&metadata_file, optarg, MODE_APPEND); 440 break; 441 case 's': 442 ret = set_file(&signature_file, optarg, MODE_EXTRACT); 443 break; 444 case 'i': 445 ret = set_file(&metadata_file, optarg, MODE_EXTRACT); 446 break; 447 case 't': 448 truncate_file = true; 449 break; 450 case 'T': 451 write_truncated = true; 452 break; 453 case 'q': 454 quiet = true; 455 break; 456 } 457 458 if (ret) 459 goto out; 460 } 461 462 if (optind >= argc) { 463 ret = usage(progname); 464 goto out; 465 } 466 467 if (file_mode == MODE_DEFAULT) { 468 ret = usage(progname); 469 goto out; 470 } 471 472 if (signature_file && metadata_file) { 473 msg("Cannot append/extract metadata and signature in one run\n"); 474 return 1; 475 } 476 477 if (file_mode) 478 ret = add_data(argv[optind]); 479 else 480 ret = extract_data(argv[optind]); 481 482 out: 483 cleanup(); 484 return ret; 485 } 486
This page was automatically generated by LXR 0.3.1. • OpenWrt