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 write_truncated;
47 static bool quiet = false;
49 static uint32_t crc_table[256];
54 fprintf(stderr, __VA_ARGS__); \
58 usage(const char *progname)
60 fprintf(stderr, "Usage: %s <options> <firmware>\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"
75 open_file(const char *name, bool write)
79 if (!strcmp(name, "-"))
80 return write ? stdout : stdin;
82 ret = fopen(name, write ? "w" : "r+");
84 ret = fopen(name, "r");
90 set_file(FILE **file, const char *name, int mode)
94 else if (file_mode != mode) {
95 msg("Error: mixing appending and extracting data is not supported\n");
100 msg("Error: the same append/extract option cannot be used multiple times\n");
104 *file = open_file(name, mode == MODE_EXTRACT);
109 trailer_update_crc(struct fwimage_trailer *tr, void *buf, int len)
111 tr->crc32 = cpu_to_be32(crc32_block(be32_to_cpu(tr->crc32), buf, len, crc_table));
115 append_data(FILE *in, FILE *out, struct fwimage_trailer *tr, int maxlen)
121 len = fread(buf, 1, sizeof(buf), in);
130 trailer_update_crc(tr, buf, len);
131 fwrite(buf, len, 1, out);
138 append_trailer(FILE *out, struct fwimage_trailer *tr)
140 tr->size = cpu_to_be32(tr->size);
141 fwrite(tr, sizeof(*tr), 1, out);
142 trailer_update_crc(tr, tr, sizeof(*tr));
146 add_metadata(struct fwimage_trailer *tr)
148 struct fwimage_header hdr = {};
150 tr->type = FWIMAGE_INFO;
151 tr->size = sizeof(hdr) + sizeof(*tr);
153 trailer_update_crc(tr, &hdr, sizeof(hdr));
154 fwrite(&hdr, sizeof(hdr), 1, firmware_file);
156 if (append_data(metadata_file, firmware_file, tr, METADATA_MAXLEN))
159 append_trailer(firmware_file, tr);
165 add_signature(struct fwimage_trailer *tr)
170 tr->type = FWIMAGE_SIGNATURE;
171 tr->size = sizeof(*tr);
173 if (append_data(signature_file, firmware_file, tr, SIGNATURE_MAXLEN))
176 append_trailer(firmware_file, tr);
182 add_data(const char *name)
184 struct fwimage_trailer tr = {
185 .magic = cpu_to_be32(FWIMAGE_MAGIC),
191 firmware_file = fopen(name, "r+");
192 if (!firmware_file) {
193 msg("Failed to open firmware file\n");
201 len = fread(buf, 1, sizeof(buf), firmware_file);
206 trailer_update_crc(&tr, buf, len);
210 ret = add_metadata(&tr);
211 else if (signature_file)
212 ret = add_signature(&tr);
215 fflush(firmware_file);
216 ftruncate(fileno(firmware_file), file_len);
223 remove_tail(struct data_buf *dbuf, int len)
225 dbuf->cur_len -= len;
226 dbuf->file_len -= len;
232 dbuf->cur = dbuf->prev;
234 dbuf->cur_len = BUFLEN;
238 extract_tail(struct data_buf *dbuf, void *dest, int len)
240 int cur_len = dbuf->cur_len;
248 memcpy(dest + (len - cur_len), dbuf->cur + dbuf->cur_len - cur_len, cur_len);
249 remove_tail(dbuf, cur_len);
251 cur_len = len - cur_len;
252 if (cur_len && !dbuf->cur)
255 memcpy(dest, dbuf->cur + dbuf->cur_len - cur_len, cur_len);
256 remove_tail(dbuf, cur_len);
262 tail_crc32(struct data_buf *dbuf, uint32_t crc32)
265 crc32 = crc32_block(crc32, dbuf->prev, BUFLEN, crc_table);
267 return crc32_block(crc32, dbuf->cur, dbuf->cur_len, crc_table);
271 validate_metadata(struct fwimage_header *hdr, int data_len)
273 if (hdr->version != 0)
279 extract_data(const char *name)
281 struct fwimage_header *hdr;
282 struct fwimage_trailer tr;
283 struct data_buf dbuf = {};
288 bool metadata_keep = false;
290 firmware_file = open_file(name, false);
291 if (!firmware_file) {
292 msg("Failed to open firmware file\n");
296 if (truncate_file && firmware_file == stdin) {
297 msg("Cannot truncate file when reading from stdin\n");
301 buf = malloc(BUFLEN);
306 char *tmp = dbuf.cur;
308 if (write_truncated && dbuf.prev)
309 fwrite(dbuf.prev, 1, BUFLEN, stdout);
311 dbuf.cur = dbuf.prev;
315 crc32 = crc32_block(crc32, dbuf.cur, BUFLEN, crc_table);
317 dbuf.cur = malloc(BUFLEN);
322 dbuf.cur_len = fread(dbuf.cur, 1, BUFLEN, firmware_file);
323 dbuf.file_len += dbuf.cur_len;
324 } while (dbuf.cur_len == BUFLEN);
328 if (extract_tail(&dbuf, &tr, sizeof(tr)))
331 data_len = be32_to_cpu(tr.size) - sizeof(tr);
332 if (tr.magic != cpu_to_be32(FWIMAGE_MAGIC)) {
333 msg("Data not found\n");
337 if (be32_to_cpu(tr.crc32) != tail_crc32(&dbuf, crc32)) {
342 if (data_len > BUFLEN) {
347 extract_tail(&dbuf, buf, data_len);
349 if (tr.type == FWIMAGE_SIGNATURE) {
352 fwrite(buf, data_len, 1, signature_file);
355 } else if (tr.type == FWIMAGE_INFO) {
356 if (!metadata_file) {
357 dbuf.file_len += data_len + sizeof(tr);
358 metadata_keep = true;
363 data_len -= sizeof(*hdr);
364 if (validate_metadata(hdr, data_len))
367 fwrite(hdr + 1, data_len, 1, metadata_file);
375 if (!ret && truncate_file)
376 ftruncate(fileno(firmware_file), dbuf.file_len);
378 if (write_truncated) {
380 fwrite(dbuf.prev, 1, BUFLEN, stdout);
382 fwrite(dbuf.cur, 1, dbuf.cur_len, stdout);
384 fwrite(buf, data_len, 1, stdout);
385 fwrite(&tr, sizeof(tr), 1, stdout);
396 static void cleanup(void)
399 fclose(signature_file);
401 fclose(metadata_file);
403 fclose(firmware_file);
406 int main(int argc, char **argv)
408 const char *progname = argv[0];
411 crc32_filltable(crc_table);
413 while ((ch = getopt(argc, argv, "i:I:qs:S:tT")) != -1) {
417 ret = set_file(&signature_file, optarg, MODE_APPEND);
420 ret = set_file(&metadata_file, optarg, MODE_APPEND);
423 ret = set_file(&signature_file, optarg, MODE_EXTRACT);
426 ret = set_file(&metadata_file, optarg, MODE_EXTRACT);
429 truncate_file = true;
432 write_truncated = true;
443 if (optind >= argc) {
444 ret = usage(progname);
448 if (file_mode == MODE_DEFAULT) {
449 ret = usage(progname);
453 if (signature_file && metadata_file) {
454 msg("Cannot append/extract metadata and signature in one run\n");
459 ret = add_data(argv[optind]);
461 ret = extract_data(argv[optind]);