123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /*
- * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <sys/types.h>
- #include <stdio.h>
- #include <getopt.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "fwimage.h"
- #include "utils.h"
- #include "crc32.h"
- #define METADATA_MAXLEN 30 * 1024
- #define SIGNATURE_MAXLEN 1 * 1024
- #define BUFLEN (METADATA_MAXLEN + SIGNATURE_MAXLEN + 1024)
- enum {
- MODE_DEFAULT = -1,
- MODE_EXTRACT = 0,
- MODE_APPEND = 1,
- };
- struct data_buf {
- char *cur;
- char *prev;
- int cur_len;
- int file_len;
- };
- static FILE *signature_file, *metadata_file, *firmware_file;
- static int file_mode = MODE_DEFAULT;
- static bool truncate_file;
- static bool write_truncated;
- static bool quiet = false;
- static uint32_t crc_table[256];
- #define msg(...) \
- do { \
- if (!quiet) \
- fprintf(stderr, __VA_ARGS__); \
- } while (0)
- static int
- usage(const char *progname)
- {
- fprintf(stderr, "Usage: %s <options> <firmware>\n"
- "\n"
- "Options:\n"
- " -S <file>: Append signature file to firmware image\n"
- " -I <file>: Append metadata file to firmware image\n"
- " -s <file>: Extract signature file from firmware image\n"
- " -i <file>: Extract metadata file from firmware image\n"
- " -t: Remove extracted chunks from firmare image (using -s, -i)\n"
- " -T: Output firmware image without extracted chunks to stdout (using -s, -i)\n"
- " -q: Quiet (suppress error messages)\n"
- "\n", progname);
- return 1;
- }
- static FILE *
- open_file(const char *name, bool write)
- {
- FILE *ret;
- if (!strcmp(name, "-"))
- return write ? stdout : stdin;
- ret = fopen(name, write ? "w" : "r+");
- if (!ret && !write)
- ret = fopen(name, "r");
- return ret;
- }
- static int
- set_file(FILE **file, const char *name, int mode)
- {
- if (file_mode < 0)
- file_mode = mode;
- else if (file_mode != mode) {
- msg("Error: mixing appending and extracting data is not supported\n");
- return 1;
- }
- if (*file) {
- msg("Error: the same append/extract option cannot be used multiple times\n");
- return 1;
- }
- *file = open_file(name, mode == MODE_EXTRACT);
- return !*file;
- }
- static void
- trailer_update_crc(struct fwimage_trailer *tr, void *buf, int len)
- {
- tr->crc32 = cpu_to_be32(crc32_block(be32_to_cpu(tr->crc32), buf, len, crc_table));
- }
- static int
- append_data(FILE *in, FILE *out, struct fwimage_trailer *tr, int maxlen)
- {
- while (1) {
- char buf[512];
- int len;
- len = fread(buf, 1, sizeof(buf), in);
- if (!len)
- break;
- maxlen -= len;
- if (maxlen < 0)
- return 1;
- tr->size += len;
- trailer_update_crc(tr, buf, len);
- fwrite(buf, len, 1, out);
- }
- return 0;
- }
- static void
- append_trailer(FILE *out, struct fwimage_trailer *tr)
- {
- tr->size = cpu_to_be32(tr->size);
- fwrite(tr, sizeof(*tr), 1, out);
- trailer_update_crc(tr, tr, sizeof(*tr));
- }
- static int
- add_metadata(struct fwimage_trailer *tr)
- {
- struct fwimage_header hdr = {};
- tr->type = FWIMAGE_INFO;
- tr->size = sizeof(hdr) + sizeof(*tr);
- trailer_update_crc(tr, &hdr, sizeof(hdr));
- fwrite(&hdr, sizeof(hdr), 1, firmware_file);
- if (append_data(metadata_file, firmware_file, tr, METADATA_MAXLEN))
- return 1;
- append_trailer(firmware_file, tr);
- return 0;
- }
- static int
- add_signature(struct fwimage_trailer *tr)
- {
- if (!signature_file)
- return 0;
- tr->type = FWIMAGE_SIGNATURE;
- tr->size = sizeof(*tr);
- if (append_data(signature_file, firmware_file, tr, SIGNATURE_MAXLEN))
- return 1;
- append_trailer(firmware_file, tr);
- return 0;
- }
- static int
- add_data(const char *name)
- {
- struct fwimage_trailer tr = {
- .magic = cpu_to_be32(FWIMAGE_MAGIC),
- .crc32 = ~0,
- };
- int file_len = 0;
- int ret = 0;
- firmware_file = fopen(name, "r+");
- if (!firmware_file) {
- msg("Failed to open firmware file\n");
- return 1;
- }
- while (1) {
- char buf[512];
- int len;
- len = fread(buf, 1, sizeof(buf), firmware_file);
- if (!len)
- break;
- file_len += len;
- trailer_update_crc(&tr, buf, len);
- }
- if (metadata_file)
- ret = add_metadata(&tr);
- else if (signature_file)
- ret = add_signature(&tr);
- if (ret) {
- fflush(firmware_file);
- ftruncate(fileno(firmware_file), file_len);
- }
- return ret;
- }
- static void
- remove_tail(struct data_buf *dbuf, int len)
- {
- dbuf->cur_len -= len;
- dbuf->file_len -= len;
- if (dbuf->cur_len)
- return;
- free(dbuf->cur);
- dbuf->cur = dbuf->prev;
- dbuf->prev = NULL;
- dbuf->cur_len = BUFLEN;
- }
- static int
- extract_tail(struct data_buf *dbuf, void *dest, int len)
- {
- int cur_len = dbuf->cur_len;
- if (!dbuf->cur)
- return 1;
- if (cur_len >= len)
- cur_len = len;
- memcpy(dest + (len - cur_len), dbuf->cur + dbuf->cur_len - cur_len, cur_len);
- remove_tail(dbuf, cur_len);
- cur_len = len - cur_len;
- if (cur_len && !dbuf->cur)
- return 1;
- memcpy(dest, dbuf->cur + dbuf->cur_len - cur_len, cur_len);
- remove_tail(dbuf, cur_len);
- return 0;
- }
- static uint32_t
- tail_crc32(struct data_buf *dbuf, uint32_t crc32)
- {
- if (dbuf->prev)
- crc32 = crc32_block(crc32, dbuf->prev, BUFLEN, crc_table);
- return crc32_block(crc32, dbuf->cur, dbuf->cur_len, crc_table);
- }
- static int
- validate_metadata(struct fwimage_header *hdr, int data_len)
- {
- if (hdr->version != 0)
- return 1;
- return 0;
- }
- static int
- extract_data(const char *name)
- {
- struct fwimage_header *hdr;
- struct fwimage_trailer tr;
- struct data_buf dbuf = {};
- uint32_t crc32 = ~0;
- int data_len = 0;
- int ret = 1;
- void *buf;
- bool metadata_keep = false;
- firmware_file = open_file(name, false);
- if (!firmware_file) {
- msg("Failed to open firmware file\n");
- return 1;
- }
- if (truncate_file && firmware_file == stdin) {
- msg("Cannot truncate file when reading from stdin\n");
- return 1;
- }
- buf = malloc(BUFLEN);
- if (!buf)
- return 1;
- do {
- char *tmp = dbuf.cur;
- if (write_truncated && dbuf.prev)
- fwrite(dbuf.prev, 1, BUFLEN, stdout);
- dbuf.cur = dbuf.prev;
- dbuf.prev = tmp;
- if (dbuf.cur)
- crc32 = crc32_block(crc32, dbuf.cur, BUFLEN, crc_table);
- else
- dbuf.cur = malloc(BUFLEN);
- if (!dbuf.cur)
- goto out;
- dbuf.cur_len = fread(dbuf.cur, 1, BUFLEN, firmware_file);
- dbuf.file_len += dbuf.cur_len;
- } while (dbuf.cur_len == BUFLEN);
- while (1) {
- if (extract_tail(&dbuf, &tr, sizeof(tr)))
- break;
- if (tr.magic != cpu_to_be32(FWIMAGE_MAGIC)) {
- msg("Data not found\n");
- metadata_keep = true;
- break;
- }
- data_len = be32_to_cpu(tr.size) - sizeof(tr);
- if (be32_to_cpu(tr.crc32) != tail_crc32(&dbuf, crc32)) {
- msg("CRC error\n");
- break;
- }
- if (data_len > BUFLEN) {
- msg("Size error\n");
- break;
- }
- extract_tail(&dbuf, buf, data_len);
- if (tr.type == FWIMAGE_SIGNATURE) {
- if (!signature_file)
- continue;
- fwrite(buf, data_len, 1, signature_file);
- ret = 0;
- break;
- } else if (tr.type == FWIMAGE_INFO) {
- if (!metadata_file) {
- dbuf.file_len += data_len + sizeof(tr);
- metadata_keep = true;
- break;
- }
- hdr = buf;
- data_len -= sizeof(*hdr);
- if (validate_metadata(hdr, data_len))
- continue;
- fwrite(hdr + 1, data_len, 1, metadata_file);
- ret = 0;
- break;
- } else {
- continue;
- }
- }
- if (!ret && truncate_file)
- ftruncate(fileno(firmware_file), dbuf.file_len);
- if (write_truncated) {
- if (dbuf.prev)
- fwrite(dbuf.prev, 1, BUFLEN, stdout);
- if (dbuf.cur)
- fwrite(dbuf.cur, 1, dbuf.cur_len, stdout);
- if (metadata_keep) {
- fwrite(buf, data_len, 1, stdout);
- fwrite(&tr, sizeof(tr), 1, stdout);
- }
- }
- out:
- free(buf);
- free(dbuf.cur);
- free(dbuf.prev);
- return ret;
- }
- static void cleanup(void)
- {
- if (signature_file)
- fclose(signature_file);
- if (metadata_file)
- fclose(metadata_file);
- if (firmware_file)
- fclose(firmware_file);
- }
- int main(int argc, char **argv)
- {
- const char *progname = argv[0];
- int ret, ch;
- crc32_filltable(crc_table);
- while ((ch = getopt(argc, argv, "i:I:qs:S:tT")) != -1) {
- ret = 0;
- switch(ch) {
- case 'S':
- ret = set_file(&signature_file, optarg, MODE_APPEND);
- break;
- case 'I':
- ret = set_file(&metadata_file, optarg, MODE_APPEND);
- break;
- case 's':
- ret = set_file(&signature_file, optarg, MODE_EXTRACT);
- break;
- case 'i':
- ret = set_file(&metadata_file, optarg, MODE_EXTRACT);
- break;
- case 't':
- truncate_file = true;
- break;
- case 'T':
- write_truncated = true;
- break;
- case 'q':
- quiet = true;
- break;
- }
- if (ret)
- goto out;
- }
- if (optind >= argc) {
- ret = usage(progname);
- goto out;
- }
- if (file_mode == MODE_DEFAULT) {
- ret = usage(progname);
- goto out;
- }
- if (signature_file && metadata_file) {
- msg("Cannot append/extract metadata and signature in one run\n");
- return 1;
- }
- if (file_mode)
- ret = add_data(argv[optind]);
- else
- ret = extract_data(argv[optind]);
- out:
- cleanup();
- return ret;
- }
|