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