3adc1e02492ccea546761dd05c033c46c5925a37
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
]);