• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/fwtool/fwtool.c

  1 /*
  2  * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
  3  *
  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
  7  *
  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.
 12  */
 13 #include <sys/types.h>
 14 #include <stdio.h>
 15 #include <getopt.h>
 16 #include <stdbool.h>
 17 #include <stdlib.h>
 18 #include <string.h>
 19 #include <unistd.h>
 20 
 21 #include "fwimage.h"
 22 #include "utils.h"
 23 #include "crc32.h"
 24 
 25 #define METADATA_MAXLEN         30 * 1024
 26 #define SIGNATURE_MAXLEN        1 * 1024
 27 
 28 #define BUFLEN                  (METADATA_MAXLEN + SIGNATURE_MAXLEN + 1024)
 29 
 30 enum {
 31         MODE_DEFAULT = -1,
 32         MODE_EXTRACT = 0,
 33         MODE_APPEND = 1,
 34 };
 35 
 36 struct data_buf {
 37         char *cur;
 38         char *prev;
 39         int cur_len;
 40         int file_len;
 41 };
 42 
 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;
 48 
 49 static uint32_t crc_table[256];
 50 
 51 #define msg(...)                                        \
 52         do {                                            \
 53                 if (!quiet)                             \
 54                         fprintf(stderr, __VA_ARGS__);   \
 55         } while (0)
 56 
 57 static int
 58 usage(const char *progname)
 59 {
 60         fprintf(stderr, "Usage: %s <options> <firmware>\n"
 61                 "\n"
 62                 "Options:\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"
 70                 "\n", progname);
 71         return 1;
 72 }
 73 
 74 static FILE *
 75 open_file(const char *name, bool write)
 76 {
 77         FILE *ret;
 78 
 79         if (!strcmp(name, "-"))
 80                 return write ? stdout : stdin;
 81 
 82         ret = fopen(name, write ? "w" : "r+");
 83         if (!ret && !write)
 84                 ret = fopen(name, "r");
 85 
 86         return ret;
 87 }
 88 
 89 static int
 90 set_file(FILE **file, const char *name, int mode)
 91 {
 92         if (file_mode < 0)
 93                 file_mode = mode;
 94         else if (file_mode != mode) {
 95                 msg("Error: mixing appending and extracting data is not supported\n");
 96                 return 1;
 97         }
 98 
 99         if (*file) {
100                 msg("Error: the same append/extract option cannot be used multiple times\n");
101                 return 1;
102         }
103 
104         *file = open_file(name, mode == MODE_EXTRACT);
105         return !*file;
106 }
107 
108 static void
109 trailer_update_crc(struct fwimage_trailer *tr, void *buf, int len)
110 {
111         tr->crc32 = cpu_to_be32(crc32_block(be32_to_cpu(tr->crc32), buf, len, crc_table));
112 }
113 
114 static int
115 append_data(FILE *in, FILE *out, struct fwimage_trailer *tr, int maxlen)
116 {
117         while (1) {
118                 char buf[512];
119                 int len;
120 
121                 len = fread(buf, 1, sizeof(buf), in);
122                 if (!len)
123                         break;
124 
125                 maxlen -= len;
126                 if (maxlen < 0)
127                         return 1;
128 
129                 tr->size += len;
130                 trailer_update_crc(tr, buf, len);
131                 fwrite(buf, len, 1, out);
132         }
133 
134         return 0;
135 }
136 
137 static void
138 append_trailer(FILE *out, struct fwimage_trailer *tr)
139 {
140         tr->size = cpu_to_be32(tr->size);
141         fwrite(tr, sizeof(*tr), 1, out);
142         trailer_update_crc(tr, tr, sizeof(*tr));
143 }
144 
145 static int
146 add_metadata(struct fwimage_trailer *tr)
147 {
148         struct fwimage_header hdr;
149 
150         tr->type = FWIMAGE_INFO;
151         tr->size = sizeof(hdr) + sizeof(*tr);
152 
153         memset(&hdr, 0, sizeof(hdr));
154         trailer_update_crc(tr, &hdr, sizeof(hdr));
155         fwrite(&hdr, sizeof(hdr), 1, firmware_file);
156 
157         if (append_data(metadata_file, firmware_file, tr, METADATA_MAXLEN))
158                 return 1;
159 
160         append_trailer(firmware_file, tr);
161 
162         return 0;
163 }
164 
165 static int
166 add_signature(struct fwimage_trailer *tr)
167 {
168         if (!signature_file)
169                 return 0;
170 
171         tr->type = FWIMAGE_SIGNATURE;
172         tr->size = sizeof(*tr);
173 
174         if (append_data(signature_file, firmware_file, tr, SIGNATURE_MAXLEN))
175                 return 1;
176 
177         append_trailer(firmware_file, tr);
178 
179         return 0;
180 }
181 
182 static int
183 add_data(const char *name)
184 {
185         struct fwimage_trailer tr;
186         int file_len = 0;
187         int ret = 0;
188 
189         memset(&tr, 0, sizeof(tr));
190 
191         tr.crc32 = ~0;
192         tr.magic = cpu_to_be32(FWIMAGE_MAGIC);
193 
194         firmware_file = fopen(name, "r+");
195         if (!firmware_file) {
196                 msg("Failed to open firmware file\n");
197                 return 1;
198         }
199 
200         while (1) {
201                 char buf[512];
202                 int len;
203 
204                 len = fread(buf, 1, sizeof(buf), firmware_file);
205                 if (!len)
206                         break;
207 
208                 file_len += len;
209                 trailer_update_crc(&tr, buf, len);
210         }
211 
212         if (metadata_file)
213                 ret = add_metadata(&tr);
214         else if (signature_file)
215                 ret = add_signature(&tr);
216 
217         if (ret) {
218                 fflush(firmware_file);
219                 ret = ftruncate(fileno(firmware_file), file_len);
220                 if (ret < 0)
221                         msg("Error during ftruncate: %m\n");
222         }
223 
224         return ret;
225 }
226 
227 static void
228 remove_tail(struct data_buf *dbuf, int len)
229 {
230         dbuf->cur_len -= len;
231         dbuf->file_len -= len;
232 
233         if (dbuf->cur_len)
234                 return;
235 
236         free(dbuf->cur);
237         dbuf->cur = dbuf->prev;
238         dbuf->prev = NULL;
239         dbuf->cur_len = BUFLEN;
240 }
241 
242 static int
243 extract_tail(struct data_buf *dbuf, void *dest, int len)
244 {
245         int cur_len = dbuf->cur_len;
246 
247         if (!dbuf->cur)
248                 return 1;
249 
250         if (cur_len >= len)
251                 cur_len = len;
252 
253         memcpy(dest + (len - cur_len), dbuf->cur + dbuf->cur_len - cur_len, cur_len);
254         remove_tail(dbuf, cur_len);
255 
256         cur_len = len - cur_len;
257         if (cur_len < 0 || !dbuf->cur)
258                 return 1;
259 
260         memcpy(dest, dbuf->cur + dbuf->cur_len - cur_len, cur_len);
261         remove_tail(dbuf, cur_len);
262 
263         return 0;
264 }
265 
266 static uint32_t
267 tail_crc32(struct data_buf *dbuf, uint32_t crc32)
268 {
269         if (dbuf->prev)
270                 crc32 = crc32_block(crc32, dbuf->prev, BUFLEN, crc_table);
271 
272         return crc32_block(crc32, dbuf->cur, dbuf->cur_len, crc_table);
273 }
274 
275 static int
276 validate_metadata(struct fwimage_header *hdr, int data_len)
277 {
278          if (hdr->version != 0)
279                  return 1;
280          return 0;
281 }
282 
283 static int
284 extract_data(const char *name)
285 {
286         struct fwimage_header *hdr;
287         struct fwimage_trailer tr;
288         struct data_buf dbuf = {};
289         uint32_t crc32 = ~0;
290         int data_len = 0;
291         int ret = 1;
292         void *buf;
293         bool metadata_keep = false;
294 
295         memset(&tr, 0, sizeof(tr));
296 
297         firmware_file = open_file(name, false);
298         if (!firmware_file) {
299                 msg("Failed to open firmware file\n");
300                 return 1;
301         }
302 
303         if (truncate_file && firmware_file == stdin) {
304                 msg("Cannot truncate file when reading from stdin\n");
305                 return 1;
306         }
307 
308         buf = malloc(BUFLEN);
309         if (!buf)
310                 return 1;
311 
312         do {
313                 char *tmp = dbuf.cur;
314 
315                 if (write_truncated && dbuf.prev)
316                         fwrite(dbuf.prev, 1, BUFLEN, stdout);
317 
318                 dbuf.cur = dbuf.prev;
319                 dbuf.prev = tmp;
320 
321                 if (dbuf.cur)
322                         crc32 = crc32_block(crc32, dbuf.cur, BUFLEN, crc_table);
323                 else
324                         dbuf.cur = malloc(BUFLEN);
325 
326                 if (!dbuf.cur)
327                         goto out;
328 
329                 dbuf.cur_len = fread(dbuf.cur, 1, BUFLEN, firmware_file);
330                 dbuf.file_len += dbuf.cur_len;
331         } while (dbuf.cur_len == BUFLEN);
332 
333         while (1) {
334 
335                 if (extract_tail(&dbuf, &tr, sizeof(tr))) {
336                         msg("unable to extract trailer header\n");
337                         break;
338                 }
339 
340                 if (tr.magic != cpu_to_be32(FWIMAGE_MAGIC)) {
341                         msg("Data not found\n");
342                         metadata_keep = true;
343                         break;
344                 }
345 
346                 data_len = be32_to_cpu(tr.size) - sizeof(tr);
347 
348                 if (be32_to_cpu(tr.crc32) != tail_crc32(&dbuf, crc32)) {
349                         msg("CRC error\n");
350                         break;
351                 }
352 
353                 if (data_len > BUFLEN) {
354                         msg("Size error\n");
355                         break;
356                 }
357 
358                 if (extract_tail(&dbuf, buf, data_len)) {
359                         msg("unable to extract trailer data\n");
360                         break;
361                 }
362 
363                 if (tr.type == FWIMAGE_SIGNATURE) {
364                         if (!signature_file)
365                                 continue;
366                         fwrite(buf, data_len, 1, signature_file);
367                         ret = 0;
368                         break;
369                 } else if (tr.type == FWIMAGE_INFO) {
370                         if (!metadata_file) {
371                                 dbuf.file_len += data_len + sizeof(tr);
372                                 metadata_keep = true;
373                                 break;
374                         }
375 
376                         hdr = buf;
377                         data_len -= sizeof(*hdr);
378                         if (validate_metadata(hdr, data_len))
379                                 continue;
380 
381                         fwrite(hdr + 1, data_len, 1, metadata_file);
382                         ret = 0;
383                         break;
384                 } else {
385                         continue;
386                 }
387         }
388 
389         if (!ret && truncate_file) {
390                 ret = ftruncate(fileno(firmware_file), dbuf.file_len);
391                 if (ret < 0) {
392                         msg("Error during ftruncate: %m\n");
393                         goto out;
394                 }
395         }
396 
397         if (write_truncated) {
398                 if (dbuf.prev)
399                         fwrite(dbuf.prev, 1, BUFLEN, stdout);
400                 if (dbuf.cur)
401                         fwrite(dbuf.cur, 1, dbuf.cur_len, stdout);
402                 if (metadata_keep) {
403                         fwrite(buf, data_len, 1, stdout);
404                         fwrite(&tr, sizeof(tr), 1, stdout);
405                 }
406         }
407 
408 out:
409         free(buf);
410         free(dbuf.cur);
411         free(dbuf.prev);
412         return ret;
413 }
414 
415 static void cleanup(void)
416 {
417         if (signature_file)
418                 fclose(signature_file);
419         if (metadata_file)
420                 fclose(metadata_file);
421         if (firmware_file)
422                 fclose(firmware_file);
423 }
424 
425 int main(int argc, char **argv)
426 {
427         const char *progname = argv[0];
428         int ret, ch;
429 
430         crc32_filltable(crc_table);
431 
432         while ((ch = getopt(argc, argv, "i:I:qs:S:tT")) != -1) {
433                 ret = 0;
434                 switch(ch) {
435                 case 'S':
436                         ret = set_file(&signature_file, optarg, MODE_APPEND);
437                         break;
438                 case 'I':
439                         ret = set_file(&metadata_file, optarg, MODE_APPEND);
440                         break;
441                 case 's':
442                         ret = set_file(&signature_file, optarg, MODE_EXTRACT);
443                         break;
444                 case 'i':
445                         ret = set_file(&metadata_file, optarg, MODE_EXTRACT);
446                         break;
447                 case 't':
448                         truncate_file = true;
449                         break;
450                 case 'T':
451                         write_truncated = true;
452                         break;
453                 case 'q':
454                         quiet = true;
455                         break;
456                 }
457 
458                 if (ret)
459                         goto out;
460         }
461 
462         if (optind >= argc) {
463                 ret = usage(progname);
464                 goto out;
465         }
466 
467         if (file_mode == MODE_DEFAULT) {
468                 ret = usage(progname);
469                 goto out;
470         }
471 
472         if (signature_file && metadata_file) {
473                 msg("Cannot append/extract metadata and signature in one run\n");
474                 return 1;
475         }
476 
477         if (file_mode)
478                 ret = add_data(argv[optind]);
479         else
480                 ret = extract_data(argv[optind]);
481 
482 out:
483         cleanup();
484         return ret;
485 }
486 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt