2 * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
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
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.
13 #include <sys/types.h>
25 #define METADATA_MAXLEN 30 * 1024
26 #define SIGNATURE_MAXLEN 1 * 1024
28 #define BUFLEN (METADATA_MAXLEN + SIGNATURE_MAXLEN + 1024)
43 static FILE *signature_file, *metadata_file, *firmware_file;
44 static int file_mode = MODE_DEFAULT;
45 static bool truncate_file;
46 static bool quiet = false;
48 static uint32_t crc_table[256];
53 fprintf(stderr, __VA_ARGS__); \
57 usage(const char *progname)
59 fprintf(stderr, "Usage: %s <options> <firmware>\n"
62 " -S <file>: Append signature file to firmware image\n"
63 " -I <file>: Append metadata file to firmware image\n"
64 " -s <file>: Extract signature file from firmware image\n"
65 " -i <file>: Extract metadata file from firmware image\n"
66 " -t: Remove extracted chunks from firmare image (using -s, -i)\n"
67 " -q: Quiet (suppress error messages)\n"
73 open_file(const char *name, bool write)
77 if (!strcmp(name, "-"))
78 return write ? stdout : stdin;
80 ret = fopen(name, write ? "w" : "r+");
82 ret = fopen(name, "r");
88 set_file(FILE **file, const char *name, int mode)
92 else if (file_mode != mode) {
93 msg("Error: mixing appending and extracting data is not supported\n");
98 msg("Error: the same append/extract option cannot be used multiple times\n");
102 *file = open_file(name, mode == MODE_EXTRACT);
107 trailer_update_crc(struct fwimage_trailer *tr, void *buf, int len)
109 tr->crc32 = cpu_to_be32(crc32_block(be32_to_cpu(tr->crc32), buf, len, crc_table));
113 append_data(FILE *in, FILE *out, struct fwimage_trailer *tr, int maxlen)
119 len = fread(buf, 1, sizeof(buf), in);
128 trailer_update_crc(tr, buf, len);
129 fwrite(buf, len, 1, out);
136 append_trailer(FILE *out, struct fwimage_trailer *tr)
138 tr->size = cpu_to_be32(tr->size);
139 fwrite(tr, sizeof(*tr), 1, out);
140 trailer_update_crc(tr, tr, sizeof(*tr));
144 add_metadata(struct fwimage_trailer *tr)
146 struct fwimage_header hdr = {};
148 tr->type = FWIMAGE_INFO;
149 tr->size = sizeof(hdr) + sizeof(*tr);
151 trailer_update_crc(tr, &hdr, sizeof(hdr));
152 fwrite(&hdr, sizeof(hdr), 1, firmware_file);
154 if (append_data(metadata_file, firmware_file, tr, METADATA_MAXLEN))
157 append_trailer(firmware_file, tr);
163 add_signature(struct fwimage_trailer *tr)
168 tr->type = FWIMAGE_SIGNATURE;
169 tr->size = sizeof(*tr);
171 if (append_data(signature_file, firmware_file, tr, SIGNATURE_MAXLEN))
174 append_trailer(firmware_file, tr);
180 add_data(const char *name)
182 struct fwimage_trailer tr = {
183 .magic = cpu_to_be32(FWIMAGE_MAGIC),
189 firmware_file = fopen(name, "r+");
190 if (!firmware_file) {
191 msg("Failed to open firmware file\n");
199 len = fread(buf, 1, sizeof(buf), firmware_file);
204 trailer_update_crc(&tr, buf, len);
208 ret = add_metadata(&tr);
209 else if (signature_file)
210 ret = add_signature(&tr);
213 fflush(firmware_file);
214 ftruncate(fileno(firmware_file), file_len);
221 remove_tail(struct data_buf *dbuf, int len)
223 dbuf->cur_len -= len;
224 dbuf->file_len -= len;
230 dbuf->cur = dbuf->prev;
232 dbuf->cur_len = BUFLEN;
236 extract_tail(struct data_buf *dbuf, void *dest, int len)
238 int cur_len = dbuf->cur_len;
246 memcpy(dest + (len - cur_len), dbuf->cur + dbuf->cur_len - cur_len, cur_len);
247 remove_tail(dbuf, cur_len);
249 cur_len = len - cur_len;
250 if (cur_len && !dbuf->cur)
253 memcpy(dest, dbuf->cur + dbuf->cur_len - cur_len, cur_len);
254 remove_tail(dbuf, cur_len);
260 tail_crc32(struct data_buf *dbuf, uint32_t crc32)
263 crc32 = crc32_block(crc32, dbuf->prev, BUFLEN, crc_table);
265 return crc32_block(crc32, dbuf->cur, dbuf->cur_len, crc_table);
269 validate_metadata(struct fwimage_header *hdr, int data_len)
271 if (hdr->version != 0)
277 extract_data(const char *name)
279 struct fwimage_header *hdr;
280 struct fwimage_trailer tr;
281 struct data_buf dbuf = {};
286 firmware_file = open_file(name, false);
287 if (!firmware_file) {
288 msg("Failed to open firmware file\n");
292 if (truncate_file && firmware_file == stdin) {
293 msg("Cannot truncate file when reading from stdin\n");
297 buf = malloc(BUFLEN);
302 char *tmp = dbuf.cur;
304 dbuf.cur = dbuf.prev;
308 crc32 = crc32_block(crc32, dbuf.cur, BUFLEN, crc_table);
310 dbuf.cur = malloc(BUFLEN);
315 dbuf.cur_len = fread(dbuf.cur, 1, BUFLEN, firmware_file);
316 dbuf.file_len += dbuf.cur_len;
317 } while (dbuf.cur_len == BUFLEN);
322 if (extract_tail(&dbuf, &tr, sizeof(tr)))
325 data_len = be32_to_cpu(tr.size) - sizeof(tr);
326 if (tr.magic != cpu_to_be32(FWIMAGE_MAGIC)) {
327 msg("Data not found\n");
331 if (be32_to_cpu(tr.crc32) != tail_crc32(&dbuf, crc32)) {
336 if (data_len > BUFLEN) {
341 extract_tail(&dbuf, buf, data_len);
343 if (tr.type == FWIMAGE_SIGNATURE) {
346 fwrite(buf, data_len, 1, signature_file);
349 } else if (tr.type == FWIMAGE_INFO) {
354 data_len -= sizeof(*hdr);
355 if (validate_metadata(hdr, data_len))
358 fwrite(hdr + 1, data_len, 1, metadata_file);
366 if (!ret && truncate_file)
367 ftruncate(fileno(firmware_file), dbuf.file_len);
376 static void cleanup(void)
379 fclose(signature_file);
381 fclose(metadata_file);
383 fclose(firmware_file);
386 int main(int argc, char **argv)
388 const char *progname = argv[0];
391 crc32_filltable(crc_table);
393 while ((ch = getopt(argc, argv, "i:I:qs:S:t")) != -1) {
397 ret = set_file(&signature_file, optarg, MODE_APPEND);
400 ret = set_file(&metadata_file, optarg, MODE_APPEND);
403 ret = set_file(&signature_file, optarg, MODE_EXTRACT);
406 ret = set_file(&metadata_file, optarg, MODE_EXTRACT);
409 truncate_file = true;
420 if (optind >= argc) {
421 ret = usage(progname);
425 if (file_mode == MODE_DEFAULT) {
426 ret = usage(progname);
430 if (signature_file && metadata_file) {
431 msg("Cannot append/extract metadata and signature in one run\n");
436 ret = add_data(argv[optind]);
438 ret = extract_data(argv[optind]);