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 ret
= ftruncate(fileno(firmware_file
), file_len
);
218 msg("Error during ftruncate: %m\n");
225 remove_tail(struct data_buf
*dbuf
, int len
)
227 dbuf
->cur_len
-= len
;
228 dbuf
->file_len
-= len
;
234 dbuf
->cur
= dbuf
->prev
;
236 dbuf
->cur_len
= BUFLEN
;
240 extract_tail(struct data_buf
*dbuf
, void *dest
, int len
)
242 int cur_len
= dbuf
->cur_len
;
250 memcpy(dest
+ (len
- cur_len
), dbuf
->cur
+ dbuf
->cur_len
- cur_len
, cur_len
);
251 remove_tail(dbuf
, cur_len
);
253 cur_len
= len
- cur_len
;
254 if (cur_len
&& !dbuf
->cur
)
257 memcpy(dest
, dbuf
->cur
+ dbuf
->cur_len
- cur_len
, cur_len
);
258 remove_tail(dbuf
, cur_len
);
264 tail_crc32(struct data_buf
*dbuf
, uint32_t crc32
)
267 crc32
= crc32_block(crc32
, dbuf
->prev
, BUFLEN
, crc_table
);
269 return crc32_block(crc32
, dbuf
->cur
, dbuf
->cur_len
, crc_table
);
273 validate_metadata(struct fwimage_header
*hdr
, int data_len
)
275 if (hdr
->version
!= 0)
281 extract_data(const char *name
)
283 struct fwimage_header
*hdr
;
284 struct fwimage_trailer tr
;
285 struct data_buf dbuf
= {};
290 bool metadata_keep
= false;
292 firmware_file
= open_file(name
, false);
293 if (!firmware_file
) {
294 msg("Failed to open firmware file\n");
298 if (truncate_file
&& firmware_file
== stdin
) {
299 msg("Cannot truncate file when reading from stdin\n");
303 buf
= malloc(BUFLEN
);
308 char *tmp
= dbuf
.cur
;
310 if (write_truncated
&& dbuf
.prev
)
311 fwrite(dbuf
.prev
, 1, BUFLEN
, stdout
);
313 dbuf
.cur
= dbuf
.prev
;
317 crc32
= crc32_block(crc32
, dbuf
.cur
, BUFLEN
, crc_table
);
319 dbuf
.cur
= malloc(BUFLEN
);
324 dbuf
.cur_len
= fread(dbuf
.cur
, 1, BUFLEN
, firmware_file
);
325 dbuf
.file_len
+= dbuf
.cur_len
;
326 } while (dbuf
.cur_len
== BUFLEN
);
330 if (extract_tail(&dbuf
, &tr
, sizeof(tr
)))
333 if (tr
.magic
!= cpu_to_be32(FWIMAGE_MAGIC
)) {
334 msg("Data not found\n");
335 metadata_keep
= true;
339 data_len
= be32_to_cpu(tr
.size
) - sizeof(tr
);
341 if (be32_to_cpu(tr
.crc32
) != tail_crc32(&dbuf
, crc32
)) {
346 if (data_len
> BUFLEN
) {
351 extract_tail(&dbuf
, buf
, data_len
);
353 if (tr
.type
== FWIMAGE_SIGNATURE
) {
356 fwrite(buf
, data_len
, 1, signature_file
);
359 } else if (tr
.type
== FWIMAGE_INFO
) {
360 if (!metadata_file
) {
361 dbuf
.file_len
+= data_len
+ sizeof(tr
);
362 metadata_keep
= true;
367 data_len
-= sizeof(*hdr
);
368 if (validate_metadata(hdr
, data_len
))
371 fwrite(hdr
+ 1, data_len
, 1, metadata_file
);
379 if (!ret
&& truncate_file
) {
380 ret
= ftruncate(fileno(firmware_file
), dbuf
.file_len
);
382 msg("Error during ftruncate: %m\n");
387 if (write_truncated
) {
389 fwrite(dbuf
.prev
, 1, BUFLEN
, stdout
);
391 fwrite(dbuf
.cur
, 1, dbuf
.cur_len
, stdout
);
393 fwrite(buf
, data_len
, 1, stdout
);
394 fwrite(&tr
, sizeof(tr
), 1, stdout
);
405 static void cleanup(void)
408 fclose(signature_file
);
410 fclose(metadata_file
);
412 fclose(firmware_file
);
415 int main(int argc
, char **argv
)
417 const char *progname
= argv
[0];
420 crc32_filltable(crc_table
);
422 while ((ch
= getopt(argc
, argv
, "i:I:qs:S:tT")) != -1) {
426 ret
= set_file(&signature_file
, optarg
, MODE_APPEND
);
429 ret
= set_file(&metadata_file
, optarg
, MODE_APPEND
);
432 ret
= set_file(&signature_file
, optarg
, MODE_EXTRACT
);
435 ret
= set_file(&metadata_file
, optarg
, MODE_EXTRACT
);
438 truncate_file
= true;
441 write_truncated
= true;
452 if (optind
>= argc
) {
453 ret
= usage(progname
);
457 if (file_mode
== MODE_DEFAULT
) {
458 ret
= usage(progname
);
462 if (signature_file
&& metadata_file
) {
463 msg("Cannot append/extract metadata and signature in one run\n");
468 ret
= add_data(argv
[optind
]);
470 ret
= extract_data(argv
[optind
]);