From: Petr Štetiar Date: Thu, 17 Oct 2019 12:47:02 +0000 (+0200) Subject: refactor into separate Git project X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=a7dc0526f81997fbdc27109452a8e4e519c4b69a;p=project%2Ffwtool.git refactor into separate Git project For improved reusability, testing etc. Signed-off-by: Petr Štetiar --- diff --git a/Makefile b/Makefile deleted file mode 100644 index 283be8d..0000000 --- a/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright (C) Felix Fietkau -# -# This is free software, licensed under the GNU General Public License v2. -# See /LICENSE for more information. -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=fwtool -PKG_RELEASE:=2 - -PKG_FLAGS:=nonshared - -PKG_MAINTAINER := Felix Fietkau -PKG_BUILD_DEPENDS := fwtool/host - -include $(INCLUDE_DIR)/package.mk -include $(INCLUDE_DIR)/host-build.mk - -HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST) - -define Package/fwtool - SECTION:=utils - CATEGORY:=Base system - TITLE:=Utility for appending and extracting firmware metadata and signatures -endef - -define Host/Compile - $(HOSTCC) $(HOST_CFLAGS) $(HOST_LDFLAGS) -o $(HOST_BUILD_DIR)/fwtool ./src/fwtool.c -endef - -define Host/Install - $(INSTALL_BIN) $(HOST_BUILD_DIR)/fwtool $(1)/bin/ -endef - -define Build/Compile - $(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) -o $(PKG_BUILD_DIR)/fwtool ./src/fwtool.c -endef - -define Package/fwtool/install - $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/fwtool $(1)/usr/bin/ -endef - -$(eval $(call HostBuild)) -$(eval $(call BuildPackage,fwtool)) diff --git a/crc32.h b/crc32.h new file mode 100644 index 0000000..022c69f --- /dev/null +++ b/crc32.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * Based on busybox code: + * CRC32 table fill function + * Copyright (C) 2006 by Rob Sullivan + * + * 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. + */ +#ifndef __BB_CRC32_H +#define __BB_CRC32_H + +static inline void +crc32_filltable(uint32_t *crc_table) +{ + uint32_t polynomial = 0xedb88320; + uint32_t c; + int i, j; + + for (i = 0; i < 256; i++) { + c = i; + for (j = 8; j; j--) + c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1); + + *crc_table++ = c; + } +} + +static inline uint32_t +crc32_block(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) +{ + const void *end = (uint8_t*)buf + len; + + while (buf != end) { + val = crc_table[(uint8_t)val ^ *(uint8_t*)buf] ^ (val >> 8); + buf = (uint8_t*)buf + 1; + } + return val; +} + +#endif diff --git a/fwimage.h b/fwimage.h new file mode 100644 index 0000000..52dcfb1 --- /dev/null +++ b/fwimage.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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. + */ +#ifndef __FWIMAGE_H +#define __FWIMAGE_H + +#include + +#define FWIMAGE_MAGIC 0x46577830 /* FWx0 */ + +struct fwimage_header { + uint32_t version; + uint32_t flags; + char data[]; +}; + +struct fwimage_trailer { + uint32_t magic; + uint32_t crc32; + uint8_t type; + uint8_t __pad[3]; + uint32_t size; +}; + +enum fwimage_type { + FWIMAGE_SIGNATURE, + FWIMAGE_INFO, +}; + +#endif diff --git a/fwtool.c b/fwtool.c new file mode 100644 index 0000000..89e8951 --- /dev/null +++ b/fwtool.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2016 Felix Fietkau + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 \n" + "\n" + "Options:\n" + " -S : Append signature file to firmware image\n" + " -I : Append metadata file to firmware image\n" + " -s : Extract signature file from firmware image\n" + " -i : 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; +} diff --git a/src/crc32.h b/src/crc32.h deleted file mode 100644 index 022c69f..0000000 --- a/src/crc32.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2016 Felix Fietkau - * - * Based on busybox code: - * CRC32 table fill function - * Copyright (C) 2006 by Rob Sullivan - * - * 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. - */ -#ifndef __BB_CRC32_H -#define __BB_CRC32_H - -static inline void -crc32_filltable(uint32_t *crc_table) -{ - uint32_t polynomial = 0xedb88320; - uint32_t c; - int i, j; - - for (i = 0; i < 256; i++) { - c = i; - for (j = 8; j; j--) - c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1); - - *crc_table++ = c; - } -} - -static inline uint32_t -crc32_block(uint32_t val, const void *buf, unsigned len, uint32_t *crc_table) -{ - const void *end = (uint8_t*)buf + len; - - while (buf != end) { - val = crc_table[(uint8_t)val ^ *(uint8_t*)buf] ^ (val >> 8); - buf = (uint8_t*)buf + 1; - } - return val; -} - -#endif diff --git a/src/fwimage.h b/src/fwimage.h deleted file mode 100644 index 52dcfb1..0000000 --- a/src/fwimage.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2016 Felix Fietkau - * - * 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. - */ -#ifndef __FWIMAGE_H -#define __FWIMAGE_H - -#include - -#define FWIMAGE_MAGIC 0x46577830 /* FWx0 */ - -struct fwimage_header { - uint32_t version; - uint32_t flags; - char data[]; -}; - -struct fwimage_trailer { - uint32_t magic; - uint32_t crc32; - uint8_t type; - uint8_t __pad[3]; - uint32_t size; -}; - -enum fwimage_type { - FWIMAGE_SIGNATURE, - FWIMAGE_INFO, -}; - -#endif diff --git a/src/fwtool.c b/src/fwtool.c deleted file mode 100644 index 89e8951..0000000 --- a/src/fwtool.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (C) 2016 Felix Fietkau - * - * 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 -#include -#include -#include -#include -#include -#include - -#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 \n" - "\n" - "Options:\n" - " -S : Append signature file to firmware image\n" - " -I : Append metadata file to firmware image\n" - " -s : Extract signature file from firmware image\n" - " -i : 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; -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index c2e665e..0000000 --- a/src/utils.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * utils - misc libubox utility functions - * - * Copyright (C) 2012 Felix Fietkau - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef __LIBUBOX_UTILS_H -#define __LIBUBOX_UTILS_H - -#include -#include -#include - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#endif - -#ifdef __GNUC__ -#define _GNUC_MIN_VER(maj, min) (((__GNUC__ << 8) + __GNUC_MINOR__) >= (((maj) << 8) + (min))) -#else -#define _GNUC_MIN_VER(maj, min) 0 -#endif - -#if defined(__linux__) || defined(__CYGWIN__) -#include -#include - -#elif defined(__APPLE__) -#include -#include -#define bswap_32(x) OSSwapInt32(x) -#define bswap_64(x) OSSwapInt64(x) -#elif defined(__FreeBSD__) -#include -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) -#else -#include -#define bswap_32(x) swap32(x) -#define bswap_64(x) swap64(x) -#endif - -#ifndef __BYTE_ORDER -#define __BYTE_ORDER BYTE_ORDER -#endif -#ifndef __BIG_ENDIAN -#define __BIG_ENDIAN BIG_ENDIAN -#endif -#ifndef __LITTLE_ENDIAN -#define __LITTLE_ENDIAN LITTLE_ENDIAN -#endif - -static inline uint16_t __u_bswap16(uint16_t val) -{ - return ((val >> 8) & 0xffu) | ((val & 0xffu) << 8); -} - -#if _GNUC_MIN_VER(4, 2) -#define __u_bswap32(x) __builtin_bswap32(x) -#define __u_bswap64(x) __builtin_bswap64(x) -#else -#define __u_bswap32(x) bswap_32(x) -#define __u_bswap64(x) bswap_64(x) -#endif - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -#define cpu_to_be64(x) __u_bswap64(x) -#define cpu_to_be32(x) __u_bswap32(x) -#define cpu_to_be16(x) __u_bswap16((uint16_t) (x)) - -#define be64_to_cpu(x) __u_bswap64(x) -#define be32_to_cpu(x) __u_bswap32(x) -#define be16_to_cpu(x) __u_bswap16((uint16_t) (x)) - -#define cpu_to_le64(x) (x) -#define cpu_to_le32(x) (x) -#define cpu_to_le16(x) (x) - -#define le64_to_cpu(x) (x) -#define le32_to_cpu(x) (x) -#define le16_to_cpu(x) (x) - -#else /* __BYTE_ORDER == __LITTLE_ENDIAN */ - -#define cpu_to_le64(x) __u_bswap64(x) -#define cpu_to_le32(x) __u_bswap32(x) -#define cpu_to_le16(x) __u_bswap16((uint16_t) (x)) - -#define le64_to_cpu(x) __u_bswap64(x) -#define le32_to_cpu(x) __u_bswap32(x) -#define le16_to_cpu(x) __u_bswap16((uint16_t) (x)) - -#define cpu_to_be64(x) (x) -#define cpu_to_be32(x) (x) -#define cpu_to_be16(x) (x) - -#define be64_to_cpu(x) (x) -#define be32_to_cpu(x) (x) -#define be16_to_cpu(x) (x) - -#endif - -#endif diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..c2e665e --- /dev/null +++ b/utils.h @@ -0,0 +1,116 @@ +/* + * utils - misc libubox utility functions + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_UTILS_H +#define __LIBUBOX_UTILS_H + +#include +#include +#include + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#ifdef __GNUC__ +#define _GNUC_MIN_VER(maj, min) (((__GNUC__ << 8) + __GNUC_MINOR__) >= (((maj) << 8) + (min))) +#else +#define _GNUC_MIN_VER(maj, min) 0 +#endif + +#if defined(__linux__) || defined(__CYGWIN__) +#include +#include + +#elif defined(__APPLE__) +#include +#include +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) +#elif defined(__FreeBSD__) +#include +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#else +#include +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) +#endif + +#ifndef __BYTE_ORDER +#define __BYTE_ORDER BYTE_ORDER +#endif +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#endif +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#endif + +static inline uint16_t __u_bswap16(uint16_t val) +{ + return ((val >> 8) & 0xffu) | ((val & 0xffu) << 8); +} + +#if _GNUC_MIN_VER(4, 2) +#define __u_bswap32(x) __builtin_bswap32(x) +#define __u_bswap64(x) __builtin_bswap64(x) +#else +#define __u_bswap32(x) bswap_32(x) +#define __u_bswap64(x) bswap_64(x) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#define cpu_to_be64(x) __u_bswap64(x) +#define cpu_to_be32(x) __u_bswap32(x) +#define cpu_to_be16(x) __u_bswap16((uint16_t) (x)) + +#define be64_to_cpu(x) __u_bswap64(x) +#define be32_to_cpu(x) __u_bswap32(x) +#define be16_to_cpu(x) __u_bswap16((uint16_t) (x)) + +#define cpu_to_le64(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) + +#define le64_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) + +#else /* __BYTE_ORDER == __LITTLE_ENDIAN */ + +#define cpu_to_le64(x) __u_bswap64(x) +#define cpu_to_le32(x) __u_bswap32(x) +#define cpu_to_le16(x) __u_bswap16((uint16_t) (x)) + +#define le64_to_cpu(x) __u_bswap64(x) +#define le32_to_cpu(x) __u_bswap32(x) +#define le16_to_cpu(x) __u_bswap16((uint16_t) (x)) + +#define cpu_to_be64(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_be16(x) (x) + +#define be64_to_cpu(x) (x) +#define be32_to_cpu(x) (x) +#define be16_to_cpu(x) (x) + +#endif + +#endif