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 memset(&hdr
, 0, sizeof(hdr
));
154 trailer_update_crc(tr
, &hdr
, sizeof(hdr
));
155 fwrite(&hdr
, sizeof(hdr
), 1, firmware_file
);
157 if (append_data(metadata_file
, firmware_file
, tr
, METADATA_MAXLEN
))
160 append_trailer(firmware_file
, tr
);
166 add_signature(struct fwimage_trailer
*tr
)
171 tr
->type
= FWIMAGE_SIGNATURE
;
172 tr
->size
= sizeof(*tr
);
174 if (append_data(signature_file
, firmware_file
, tr
, SIGNATURE_MAXLEN
))
177 append_trailer(firmware_file
, tr
);
183 add_data(const char *name
)
185 struct fwimage_trailer tr
;
189 memset(&tr
, 0, sizeof(tr
));
192 tr
.magic
= cpu_to_be32(FWIMAGE_MAGIC
);
194 firmware_file
= fopen(name
, "r+");
195 if (!firmware_file
) {
196 msg("Failed to open firmware file\n");
204 len
= fread(buf
, 1, sizeof(buf
), firmware_file
);
209 trailer_update_crc(&tr
, buf
, len
);
213 ret
= add_metadata(&tr
);
214 else if (signature_file
)
215 ret
= add_signature(&tr
);
218 fflush(firmware_file
);
219 ret
= ftruncate(fileno(firmware_file
), file_len
);
221 msg("Error during ftruncate: %m\n");
228 remove_tail(struct data_buf
*dbuf
, int len
)
230 dbuf
->cur_len
-= len
;
231 dbuf
->file_len
-= len
;
237 dbuf
->cur
= dbuf
->prev
;
239 dbuf
->cur_len
= BUFLEN
;
243 extract_tail(struct data_buf
*dbuf
, void *dest
, int len
)
245 int cur_len
= dbuf
->cur_len
;
253 memcpy(dest
+ (len
- cur_len
), dbuf
->cur
+ dbuf
->cur_len
- cur_len
, cur_len
);
254 remove_tail(dbuf
, cur_len
);
256 cur_len
= len
- cur_len
;
257 if (cur_len
< 0 || !dbuf
->cur
)
260 memcpy(dest
, dbuf
->cur
+ dbuf
->cur_len
- cur_len
, cur_len
);
261 remove_tail(dbuf
, cur_len
);
267 tail_crc32(struct data_buf
*dbuf
, uint32_t crc32
)
270 crc32
= crc32_block(crc32
, dbuf
->prev
, BUFLEN
, crc_table
);
272 return crc32_block(crc32
, dbuf
->cur
, dbuf
->cur_len
, crc_table
);
276 validate_metadata(struct fwimage_header
*hdr
, int data_len
)
278 if (hdr
->version
!= 0)
284 extract_data(const char *name
)
286 struct fwimage_header
*hdr
;
287 struct fwimage_trailer tr
;
288 struct data_buf dbuf
= {};
293 bool metadata_keep
= false;
295 memset(&tr
, 0, sizeof(tr
));
297 firmware_file
= open_file(name
, false);
298 if (!firmware_file
) {
299 msg("Failed to open firmware file\n");
303 if (truncate_file
&& firmware_file
== stdin
) {
304 msg("Cannot truncate file when reading from stdin\n");
308 buf
= malloc(BUFLEN
);
313 char *tmp
= dbuf
.cur
;
315 if (write_truncated
&& dbuf
.prev
)
316 fwrite(dbuf
.prev
, 1, BUFLEN
, stdout
);
318 dbuf
.cur
= dbuf
.prev
;
322 crc32
= crc32_block(crc32
, dbuf
.cur
, BUFLEN
, crc_table
);
324 dbuf
.cur
= malloc(BUFLEN
);
329 dbuf
.cur_len
= fread(dbuf
.cur
, 1, BUFLEN
, firmware_file
);
330 dbuf
.file_len
+= dbuf
.cur_len
;
331 } while (dbuf
.cur_len
== BUFLEN
);
335 if (extract_tail(&dbuf
, &tr
, sizeof(tr
))) {
336 msg("unable to extract trailer header\n");
340 if (tr
.magic
!= cpu_to_be32(FWIMAGE_MAGIC
)) {
341 msg("Data not found\n");
342 metadata_keep
= true;
346 data_len
= be32_to_cpu(tr
.size
) - sizeof(tr
);
348 if (be32_to_cpu(tr
.crc32
) != tail_crc32(&dbuf
, crc32
)) {
353 if (data_len
> BUFLEN
) {
358 if (extract_tail(&dbuf
, buf
, data_len
)) {
359 msg("unable to extract trailer data\n");
363 if (tr
.type
== FWIMAGE_SIGNATURE
) {
366 fwrite(buf
, data_len
, 1, signature_file
);
369 } else if (tr
.type
== FWIMAGE_INFO
) {
370 if (!metadata_file
) {
371 dbuf
.file_len
+= data_len
+ sizeof(tr
);
372 metadata_keep
= true;
377 data_len
-= sizeof(*hdr
);
378 if (validate_metadata(hdr
, data_len
))
381 fwrite(hdr
+ 1, data_len
, 1, metadata_file
);
389 if (!ret
&& truncate_file
) {
390 ret
= ftruncate(fileno(firmware_file
), dbuf
.file_len
);
392 msg("Error during ftruncate: %m\n");
397 if (write_truncated
) {
399 fwrite(dbuf
.prev
, 1, BUFLEN
, stdout
);
401 fwrite(dbuf
.cur
, 1, dbuf
.cur_len
, stdout
);
403 fwrite(buf
, data_len
, 1, stdout
);
404 fwrite(&tr
, sizeof(tr
), 1, stdout
);
415 static void cleanup(void)
418 fclose(signature_file
);
420 fclose(metadata_file
);
422 fclose(firmware_file
);
425 int main(int argc
, char **argv
)
427 const char *progname
= argv
[0];
430 crc32_filltable(crc_table
);
432 while ((ch
= getopt(argc
, argv
, "i:I:qs:S:tT")) != -1) {
436 ret
= set_file(&signature_file
, optarg
, MODE_APPEND
);
439 ret
= set_file(&metadata_file
, optarg
, MODE_APPEND
);
442 ret
= set_file(&signature_file
, optarg
, MODE_EXTRACT
);
445 ret
= set_file(&metadata_file
, optarg
, MODE_EXTRACT
);
448 truncate_file
= true;
451 write_truncated
= true;
462 if (optind
>= argc
) {
463 ret
= usage(progname
);
467 if (file_mode
== MODE_DEFAULT
) {
468 ret
= usage(progname
);
472 if (signature_file
&& metadata_file
) {
473 msg("Cannot append/extract metadata and signature in one run\n");
478 ret
= add_data(argv
[optind
]);
480 ret
= extract_data(argv
[optind
]);