otrx: avoid unneeded fseek() when calculating CRC32
[project/firmware-utils.git] / src / mkzyxelzldfw.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2020 Vincent Wiemann <vw@derowe.com>
4 *
5 * This program is derived from ZyXEL's GPL U-Boot code.
6 * Copyright (C) 2011-2011 ZyXEL Communications, Corp.
7 */
8
9 #define _POSIX_SOURCE
10 #define _POSIX_C_SOURCE 199309L /* getopt */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdint.h>
14 #include <time.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <netinet/in.h>
20 #include <limits.h>
21 #include "md5.h"
22
23 #define ZYXEL_MAGIC 0xdeadbeaf
24 #define MAX_MODELS 5
25 #define CHECKSUM_SIZE sizeof(uint32_t)
26 #define MAX_FILES 32
27 #define MAX_FILENAME 64
28
29 #define DATE_SIZE 32
30 #define REV_SIZE 32
31
32 #define error(fmt, ...) \
33 do { \
34 printf("Error: " fmt, ##__VA_ARGS__); \
35 exit(1); \
36 } while (0)
37
38 enum {
39 FILE_TYPE_BM = 1,
40 FILE_TYPE_KERNEL,
41 FILE_TYPE_CORE,
42 FILE_TYPE_DB,
43 FILE_TYPE_CONF,
44 FILE_TYPE_WTP
45 };
46
47 struct file_type_tbl {
48 int type;
49 char *str;
50 };
51
52 struct file_type_tbl file_types[] = {
53 { FILE_TYPE_BM, "bm" }, { FILE_TYPE_KERNEL, "kernel" },
54 { FILE_TYPE_CORE, "core" }, { FILE_TYPE_DB, "db" },
55 { FILE_TYPE_CONF, "conf", }, { FILE_TYPE_WTP, "wtp" }
56 };
57 static int FILE_TYPE_COUNT = sizeof(file_types) / sizeof(struct file_type_tbl);
58
59 struct fw_header {
60 uint32_t checksum; /* MD5-based checksum */
61 uint32_t magic; /* 0xdeadbeaf */
62 uint16_t version; /* version of the firmware archive */
63 uint16_t files_count; /* number of files contained */
64 uint16_t models_count; /* number of supported models */
65 uint16_t models[MAX_MODELS]; /* supported models' IDs */
66 uint32_t total_length; /* total length of the firmware archive */
67 uint16_t files_offset; /* offset of the first file */
68 uint16_t header_length; /* length of this header */
69 uint16_t info_length; /* length of the file information header */
70 uint16_t __padding1[3]; /* reserved for future use */
71 char capwap_version[32]; /* e.g. "undefined" */
72 char model_name[32]; /* e.g. "NWA512X-FAT" */
73 } __attribute__((packed));
74
75 struct fw_header_file {
76 uint16_t type; /* file type (e.g. FILE_TYPE_KERNEL) */
77 uint16_t flags; /* unknown */
78 uint32_t length; /* length of the file */
79 uint32_t checksum; /* checksum of the file */
80 uint32_t flash_offset; /* write offset from beginning of flash */
81 char filename[MAX_FILENAME]; /* original file name */
82 char target[128]; /* target "file" name (e.g. "kernel", "zldfs") */
83 char revision[REV_SIZE]; /* revision string */
84 char date[32]; /* creation date string */
85 } __attribute__((packed));
86
87 struct fw_header_kernel {
88 char bm_version[64];
89 char kernel_version[64];
90 char core_version[64];
91 char capwap_version[32];
92 char model_name[32];
93 uint32_t bm_checksum;
94 uint32_t kernel_checksum;
95 uint32_t zld_checksum;
96 uint32_t core_checksum;
97 uint16_t max_models;
98 uint16_t models[MAX_MODELS];
99 char padding[512 - 64 * 4 - 4 * 4 - 2 - MAX_MODELS * 2 - 4];
100 uint32_t baudrate;
101 } __attribute__((packed));
102
103 struct firmware_file {
104 struct fw_header_file header;
105 size_t offset;
106 char filepath[PATH_MAX];
107 };
108
109 struct firmware {
110 struct fw_header header;
111 struct firmware_file *files;
112 size_t files_count;
113 struct fw_header_kernel kernel_header;
114 char lower_endian;
115 };
116
117 static size_t get_file_size(FILE *fp)
118 {
119 size_t pos = (size_t)ftell(fp);
120 size_t file_size;
121
122 fseek(fp, 0, SEEK_END);
123 file_size = (size_t)ftell(fp);
124 fseek(fp, (long int)pos, SEEK_SET);
125
126 return file_size;
127 }
128
129 static void copy_from_to_file(FILE *fp_src, size_t src_offset, FILE *fp_dst,
130 size_t dst_offset, size_t length)
131 {
132 int buf[512];
133 size_t len;
134
135 if (src_offset)
136 fseek(fp_src, (long int)src_offset, SEEK_SET);
137
138 if (dst_offset)
139 fseek(fp_src, (long int)dst_offset, SEEK_SET);
140
141 while (length) {
142 if (length >= sizeof(buf)) {
143 len = sizeof(buf);
144 } else {
145 len = length;
146 }
147
148 length -= len;
149
150 if (fread(buf, len, 1, fp_src) != 1)
151 error("Failed to read");
152
153 if (fwrite(buf, len, 1, fp_dst) != 1)
154 error("Failed to write");
155 }
156 }
157
158 static void extract_to_file(FILE *fp_src, char *dst, size_t length,
159 size_t offset)
160 {
161 FILE *fp_dst;
162
163 if (!(fp_dst = fopen(dst, "wb")))
164 error("Failed to open %s for writing", dst);
165
166 copy_from_to_file(fp_src, offset, fp_dst, 0, length);
167
168 fclose(fp_dst);
169 }
170
171 static void dump_firmware_header(struct fw_header *header_p)
172 {
173 int i;
174
175 printf("FIRMWARE HEADER\n");
176 printf("checksum = 0x%08x, magic = 0x%08x\n",
177 header_p->checksum, header_p->magic);
178 printf("version = 0x%04x, files_count = %12d\n",
179 header_p->version, header_p->files_count);
180 printf("models_count = %12d, total_length = %12d\n",
181 header_p->models_count, header_p->total_length);
182 printf("files_offset = 0x%04x, header_length = %12d\n",
183 header_p->files_offset, header_p->header_length);
184 printf("info_length = %12d, capwap_version = %12s\n",
185 header_p->info_length, header_p->capwap_version);
186 printf("model_name = %s\n", header_p->model_name);
187 printf("models =");
188 for (i = 0; i < header_p->models_count && i < MAX_MODELS; i++)
189 printf(" 0x%04x", header_p->models[i]);
190 printf("\n\n");
191 }
192
193 static int get_file_type_id(char *type)
194 {
195 struct file_type_tbl *ft = file_types;
196 int i;
197
198 for (i = 0; i < FILE_TYPE_COUNT; i++, ft++)
199 if (!strcmp(type, ft->str))
200 return ft->type;
201
202 printf("Supported file types:\n");
203 for (i = 0, ft = file_types; i < FILE_TYPE_COUNT; i++, ft++)
204 printf("%8s (ID 0x%x)\n", ft->str, ft->type);
205
206 error("Unknown file type \"%s\"\n", type);
207
208 return 0;
209 }
210
211 static char *get_file_type_string(int type)
212 {
213 struct file_type_tbl *ft = file_types;
214 int i;
215
216 for (i = 0; i < FILE_TYPE_COUNT; i++, ft++)
217 if (ft->type == type)
218 return ft->str;
219
220 return NULL;
221 }
222
223 static void dump_file_header(struct fw_header_file *file_header)
224 {
225 printf("\nfilename=%s, type=%s, flags=0x%x target=%s, revision=%s\n",
226 file_header->filename, get_file_type_string(file_header->type),
227 file_header->flags, file_header->target, file_header->revision);
228 printf("date=%s, length=%u, checksum=0x%08x, flash_offset=0x%08x\n",
229 file_header->date, file_header->length, file_header->checksum,
230 file_header->flash_offset);
231 }
232
233 static void dump_kernel_header(struct fw_header_kernel *kernel_header)
234 {
235 int i;
236
237 printf("KERNEL HEADER (%lu bytes)\n", sizeof(struct fw_header_kernel));
238 printf("bm_version = %s\n", kernel_header->bm_version);
239 printf("kernel_version = %s\n", kernel_header->kernel_version);
240 printf("core_version = %s\n", kernel_header->core_version);
241 printf("capwap_version = %s\n", kernel_header->capwap_version);
242 printf("model_name = %s\n", kernel_header->model_name);
243 printf("bm_checksum = 0x%08x\n", kernel_header->bm_checksum);
244 printf("kernel_checksum = 0x%08x\n", kernel_header->kernel_checksum);
245 printf("zld_checksum = 0x%08x\n", kernel_header->zld_checksum);
246 printf("core_checksum = 0x%08x\n", kernel_header->core_checksum);
247 printf("max_models = %u\n", kernel_header->max_models);
248 printf("models =");
249 for (i = 0; i < 5; i++)
250 printf(" 0x%04x", kernel_header->models[i]);
251 printf("\n");
252 printf("baudrate = %u\n", kernel_header->baudrate);
253 printf("\n");
254 }
255
256 static void translate_fw_header(struct fw_header *header_p)
257 {
258 int i;
259
260 header_p->magic = ntohl(header_p->magic);
261 header_p->version = ntohs(header_p->version);
262 header_p->files_count = ntohs(header_p->files_count);
263 header_p->models_count = ntohs(header_p->models_count);
264 for (i = 0; i < MAX_MODELS; i++)
265 header_p->models[i] = ntohs(header_p->models[i]);
266 header_p->total_length = ntohl(header_p->total_length);
267 header_p->files_offset = ntohs(header_p->files_offset);
268 header_p->header_length = ntohs(header_p->header_length);
269 header_p->info_length = ntohs(header_p->info_length);
270 }
271
272 static void translate_file_header(struct fw_header_file *file_header)
273 {
274 file_header->type = ntohs(file_header->type);
275 file_header->flags = ntohs(file_header->flags);
276 file_header->length = ntohl(file_header->length);
277 file_header->checksum = ntohl(file_header->checksum);
278 file_header->flash_offset = ntohl(file_header->flash_offset);
279 }
280
281 static void translate_kernel_header(struct fw_header_kernel *kernel_header)
282 {
283 int i;
284
285 kernel_header->bm_checksum = ntohl(kernel_header->bm_checksum);
286 kernel_header->kernel_checksum = ntohl(kernel_header->kernel_checksum);
287 /*kernel_header->zld_checksum = ntohl(kernel_header->zld_checksum);*/
288 kernel_header->core_checksum = ntohl(kernel_header->core_checksum);
289
290 kernel_header->max_models = ntohs(kernel_header->max_models);
291 for (i = 0; i < MAX_MODELS; i++)
292 kernel_header->models[i] = ntohs(kernel_header->models[i]);
293 kernel_header->baudrate = ntohl(kernel_header->baudrate);
294 }
295
296 static void checksum_add_from_buf(MD5_CTX *ctx, void *buf,
297 size_t length, size_t offset)
298 {
299 char *begin = &((char *)buf)[offset];
300
301 MD5_Update(ctx, begin, length);
302 }
303
304 static void checksum_add_from_file(MD5_CTX *ctx, FILE *fp_src,
305 size_t length, size_t offset)
306 {
307 int buf[512];
308 size_t len;
309
310 fseek(fp_src, (long int)offset, SEEK_SET);
311 while (length) {
312 if (length >= sizeof(buf)) {
313 len = sizeof(buf);
314 } else {
315 len = length;
316 }
317
318 length -= len;
319
320 if (fread(buf, len, 1, fp_src) != 1)
321 error("Failed to read for checksum calculation");
322
323 checksum_add_from_buf(ctx, buf, len, 0);
324 }
325 }
326
327 static uint32_t checksum_finish(MD5_CTX *ctx)
328 {
329 unsigned char md5sum[16];
330 uint32_t checksum = 0;
331 int i;
332
333 MD5_Final(md5sum, ctx);
334
335 for (i = 0; i < 16; i += 4)
336 checksum += ((uint32_t)md5sum[i] << 24 |
337 (uint32_t)md5sum[i + 1] << 16 |
338 (uint32_t)md5sum[i + 2] << 8 |
339 (uint32_t)md5sum[i + 3]);
340
341 if (checksum == 0)
342 checksum = 1;
343
344 return checksum;
345 }
346
347 static uint32_t checksum_calculate(FILE *fp, size_t kernel_offset)
348 {
349 struct fw_header_kernel dummy;
350 MD5_CTX ctx;
351 size_t file_size;
352
353 fseek(fp, 0, SEEK_SET);
354 file_size = get_file_size(fp);
355
356 MD5_Init(&ctx);
357
358 checksum_add_from_file(&ctx, fp, kernel_offset - CHECKSUM_SIZE,
359 CHECKSUM_SIZE);
360
361 /* use a zeroed out kernel version header */
362 memset(&dummy, 0, sizeof(dummy));
363 checksum_add_from_buf(&ctx, &dummy, sizeof(dummy), 0);
364
365 checksum_add_from_file(&ctx, fp,
366 file_size - kernel_offset - sizeof(dummy),
367 kernel_offset + sizeof(dummy));
368
369 return checksum_finish(&ctx);
370 }
371
372 static uint32_t checksum_calculate_file(char *filename)
373 {
374 MD5_CTX ctx;
375 FILE *fp;
376 size_t file_size;
377
378 if (!(fp = fopen(filename, "rb")))
379 error("Failed to open %s for writing\n", filename);
380
381 file_size = get_file_size(fp);
382
383 MD5_Init(&ctx);
384
385 checksum_add_from_file(&ctx, fp, file_size, 0);
386
387 fclose(fp);
388
389 return checksum_finish(&ctx);
390 }
391
392 static void parse_firmware(struct firmware *fw, FILE *fp)
393 {
394 struct firmware_file *file;
395 size_t file_size, file_offset, kernel_offset;
396 uint32_t checksum = 0;
397 int i;
398
399 if (!fw)
400 error("Failed to allocate firmware struct\n");
401
402 file_size = get_file_size(fp);
403
404 if (file_size < sizeof(fw->header))
405 error("File too small\n");
406
407 if (1 != fread(&fw->header, sizeof(fw->header), 1, fp))
408 error("Failed to read firmware header\n");
409
410 if (ntohl(fw->header.magic) == ZYXEL_MAGIC) {
411 fw->lower_endian = 1;
412 translate_fw_header(&fw->header);
413 } else if (fw->header.magic != ZYXEL_MAGIC) {
414 error("Unsupported magic. Expected 0x%x, but found 0x%x\n",
415 ZYXEL_MAGIC, fw->header.magic);
416 }
417
418 if (fw->header.models_count != MAX_MODELS)
419 error("Wrong number of models. Expected %u, but found %u\n",
420 MAX_MODELS, fw->header.models_count);
421
422 dump_firmware_header(&fw->header);
423
424 if (fw->header.total_length != file_size)
425 error("File size does not match. Expected %lu, but found %u\n",
426 file_size, fw->header.total_length);
427
428 file_offset = sizeof(fw->header) +
429 fw->header.files_count * sizeof(struct fw_header_file);
430
431 if (file_offset != fw->header.files_offset)
432 error("File offset does not match definition in header\n");
433
434 if (file_size < file_offset)
435 error("File too small for %u file headers\n",
436 fw->header.files_count);
437
438 if (NULL == (fw->files = malloc(fw->header.files_count *
439 sizeof(struct firmware_file))))
440 error("Failed to allocate memory for %u file structs\n",
441 fw->header.files_count);
442
443 for (i = 0, file = fw->files; i < fw->header.files_count; i++, file++) {
444 if (1 != fread(file, sizeof(struct fw_header_file), 1, fp))
445 error("Failed to read file header #%u\n", i + 1);
446
447 if (fw->lower_endian)
448 translate_file_header(&file->header);
449
450 if (file_offset + file->header.length > fw->header.total_length)
451 error("File offset exceeds size of firmware archive\n");
452
453 if (file->header.type == FILE_TYPE_KERNEL)
454 kernel_offset = file_offset;
455
456 file->offset = file_offset;
457
458 file_offset += file->header.length;
459 }
460
461 if (!kernel_offset)
462 error("Kernel image missing for checksum calculation\n");
463
464 /* as we know the kernel offset, we can calculate the checksum
465 * as it must be excluded from checksum calculation */
466 checksum = checksum_calculate(fp, kernel_offset);
467
468 if (fw->lower_endian)
469 checksum = ntohl(checksum);
470
471 if (checksum != fw->header.checksum)
472 printf("WARN: Checksum mismatch. Calculated 0x%x\n", checksum);
473
474 fseek(fp, (long int)kernel_offset, SEEK_SET);
475 if (1 != fread(&fw->kernel_header, sizeof(fw->kernel_header), 1, fp))
476 error("Failed to read kernel header\n");
477
478 if (fw->lower_endian)
479 translate_kernel_header(&fw->kernel_header);
480
481 dump_kernel_header(&fw->kernel_header);
482 }
483
484 static void extract_firmware(struct firmware *fw, char *filename)
485 {
486 struct firmware_file *file;
487 FILE *fp;
488 int i;
489
490 if (!(fp = fopen(filename, "rb")))
491 error("Failed to open firmware archive for extraction %s\n",
492 filename);
493
494 parse_firmware(fw, fp);
495
496 printf("Extracting files...");
497
498 for (i = 0, file = fw->files; i < fw->header.files_count; i++, file++) {
499 dump_file_header(&file->header);
500 if (file->header.type == FILE_TYPE_KERNEL) {
501 /* strip kernel header */
502 extract_to_file(
503 fp, file->header.filename,
504 file->header.length -
505 sizeof(struct fw_header_kernel),
506 file->offset + sizeof(struct fw_header_kernel));
507 } else {
508 extract_to_file(fp, file->header.filename,
509 file->header.length, file->offset);
510 }
511
512 printf("Calculated file checksum is 0x%08x\n",
513 checksum_calculate_file(file->header.filename));
514 }
515
516 free(fw->files);
517 fclose(fp);
518 }
519
520 static void init_fw_header(struct firmware *fw)
521 {
522 int i;
523
524 for (i = 0; i < MAX_MODELS; i++)
525 fw->header.models[i] = 0xffff;
526
527 fw->kernel_header.baudrate = 115200;
528 fw->kernel_header.max_models = MAX_MODELS;
529 /* ZyXEL messed up their code and included a 32 bit pointer */
530 fw->header.header_length = sizeof(fw->header) + 4;
531 fw->header.magic = ZYXEL_MAGIC;
532 }
533
534 static void write_headers(FILE *fp, struct firmware *fw)
535 {
536 struct firmware_file *file;
537 unsigned int i;
538
539 fseek(fp, 0, SEEK_SET);
540
541 if (1 != fwrite(&fw->header, sizeof(fw->header), 1, fp))
542 error("Failed to write firmware header\n");
543
544 for (i = 0, file = fw->files; i < fw->files_count; i++, file++)
545 if (1 !=
546 fwrite(&file->header, sizeof(struct fw_header_file), 1, fp))
547 error("Failed to write file header #%u\n", i + 1);
548 }
549
550 static void usage(char *progname)
551 {
552 printf("Usage: %s\n"
553 " For extraction:\n"
554 " -e <file> extract firmware <file>\n\n"
555 " For creation:\n"
556 " -v <version> set hexadecimal firmware <version>\n"
557 " -b <checksum> set hexadecimal bootmanager <checksum>\n"
558 " -c <version> set capwap <version> string\n"
559 " -m <model> set <model> string\n"
560 " -d <model_id> set (up to %u) hexadecimal <model_id>\n"
561 " (multiple input files)\n"
562 " -i <file> add input <file>\n"
563 " -o <offset> set hexadecimal flash offset for file\n"
564 " -r <revision> set revision string for file\n"
565 " -t <type> choose file <type>\n"
566 " -x <target> set (partition) <target> of file\n"
567 " <output_filename>\n",
568 progname, MAX_MODELS);
569 exit(1);
570 }
571
572 int main(int argc, char *argv[])
573 {
574 struct firmware fw;
575 struct firmware_file *file;
576 struct firmware_file *core_file = NULL;
577 struct stat attr;
578 static const char *optstr = "e:v:b:c:m:d:i:o:r:t:x:h";
579 const char *capwap_version = "undefined";
580 const char *separator = " | ";
581 char *filename;
582 FILE *fp_src, *fp_dst;
583 size_t kernel_offset = 0;
584 unsigned int i;
585 int opt;
586
587 memset(&fw, 0, sizeof(fw));
588 init_fw_header(&fw);
589
590 if (argc < 1)
591 usage(argv[0]);
592
593 opt = getopt(argc, argv, optstr);
594 if (opt == -1)
595 usage(argv[0]);
596 while (opt != -1) {
597 if (optarg == NULL)
598 usage(argv[0]);
599
600 switch (opt) {
601 case 'e':
602 extract_firmware(&fw, optarg);
603 exit(0);
604 case 'v':
605 fw.header.version = (uint16_t)strtol(optarg, NULL, 16);
606 if (!fw.header.version)
607 error("Invalid version number\n");
608 break;
609 case 'b':
610 fw.kernel_header.bm_checksum =
611 (uint32_t)strtol(optarg, NULL, 16);
612 break;
613 case 'c':
614 strncpy(fw.header.capwap_version, optarg,
615 sizeof(fw.header.capwap_version) - 1);
616 break;
617 case 'm':
618 strncpy(fw.header.model_name, optarg,
619 sizeof(fw.header.model_name) - 1);
620 break;
621 case 'd':
622 if (fw.header.models_count == MAX_MODELS)
623 error("Max. number of supported models is %u\n",
624 MAX_MODELS);
625
626 fw.header.models[fw.header.models_count] =
627 (uint16_t)strtol(optarg, NULL, 16);
628 fw.header.models_count++;
629 break;
630 case 'i':
631 filename = optarg;
632
633 if (!fw.files &&
634 NULL == (fw.files = malloc(
635 MAX_FILES *
636 sizeof(struct firmware_file))))
637 error("Failed to allocate %u file structs\n",
638 MAX_FILES);
639
640 if (fw.files_count == MAX_FILES)
641 error("Maximum number of files reached (%u)\n",
642 MAX_FILES);
643
644 if (stat(optarg, &attr))
645 error("Stat failed on %s\n", optarg);
646
647 strftime(fw.files[fw.files_count].header.date,
648 DATE_SIZE - 1, "%Y-%m-%d %H:%M:%S",
649 localtime(&attr.st_mtime));
650
651 strncpy(fw.files[fw.files_count].filepath, optarg,
652 PATH_MAX - 1);
653
654 filename = strrchr(optarg, '/');
655 if (!filename)
656 filename = optarg;
657
658 strncpy(fw.files[fw.files_count].header.filename,
659 filename, MAX_FILENAME - 1);
660
661 fw.files_count++;
662 break;
663 case 'o':
664 if (!fw.files_count)
665 error("Specify offset after filename\n");
666
667 fw.files[fw.files_count - 1].header.flash_offset =
668 (uint32_t)strtol(optarg, NULL, 16);
669 break;
670 case 'r':
671 if (!fw.files_count)
672 error("Specify file revision after filename\n");
673
674 strncpy(fw.files[fw.files_count - 1].header.revision,
675 optarg, REV_SIZE - 1);
676 break;
677 case 't':
678 if (!fw.files_count)
679 error("Specify file type after filename!\n");
680
681 fw.files[fw.files_count - 1].header.type =
682 get_file_type_id(optarg);
683 break;
684 case 'x':
685 if (!fw.files_count)
686 error("Specify file target after filename!\n");
687 strncpy(fw.files[fw.files_count - 1].header.target,
688 optarg, sizeof(fw.files[0].header.target) - 1);
689 break;
690 case 'h':
691 default:
692 usage(argv[0]);
693 }
694
695 opt = getopt(argc, argv, optstr);
696 }
697
698 if (!fw.header.models_count)
699 error("Supported model IDs missing (option -d)\n");
700
701 if (!fw.header.version)
702 error("Version number missing (e.g. -v 0x100)\n");
703
704 if (!fw.kernel_header.bm_checksum)
705 error("Bootmanager checksum is missing (option -b)\n");
706
707 if (!strlen(fw.header.model_name))
708 error("Model name missing (option -m)\n");
709
710 if (!strlen(fw.header.capwap_version))
711 strncpy(fw.header.capwap_version, capwap_version,
712 sizeof(fw.header.capwap_version) - 1);
713
714 fw.header.models_count = MAX_MODELS;
715 fw.header.files_count = fw.files_count;
716 memcpy(fw.kernel_header.models, fw.header.models,
717 sizeof(fw.header.models));
718
719 for (i = 0; i < fw.files_count; i++) {
720 if (!fw.files[i].header.type)
721 error("No file or type specified for file %s\n",
722 fw.files[i].filepath);
723
724 if (!strlen(fw.files[i].header.target))
725 error("Target missing for %s (e.g. -x zldfs)\n",
726 fw.files[i].filepath);
727
728 if (!strlen(fw.files[i].header.revision))
729 error("Revision missing for %s\n",
730 fw.files[i].filepath);
731 }
732
733 filename = argv[optind];
734 if (!(fp_dst = fopen(filename, "w+b")))
735 error("Failed to open %s for writing\n", filename);
736
737 write_headers(fp_dst, &fw);
738
739 fw.header.info_length = sizeof(struct fw_header_file);
740 fw.header.files_offset =
741 sizeof(fw.header) + fw.files_count * fw.header.info_length;
742 if ((size_t)ftell(fp_dst) != fw.header.files_offset)
743 error("Oops. Something went wrong writing the file headers");
744
745 fw.header.total_length = fw.header.files_offset;
746 for (i = 0, file = fw.files; i < fw.files_count; i++, file++) {
747 file->header.checksum = checksum_calculate_file(file->filepath);
748
749 if (!(fp_src = fopen(file->filepath, "rb")))
750 error("Failed to open %s for writing\n", filename);
751
752 file->offset = fw.header.total_length;
753
754 file->header.length = get_file_size(fp_src);
755
756 if (file->header.type == FILE_TYPE_KERNEL)
757 if (1 != fwrite(&fw.kernel_header,
758 sizeof(fw.kernel_header), 1, fp_dst))
759 error("Failed to write kernel header\n");
760
761 copy_from_to_file(fp_src, 0, fp_dst, 0, file->header.length);
762
763 if (file->header.type == FILE_TYPE_KERNEL) {
764 file->header.length += sizeof(fw.kernel_header);
765 kernel_offset = file->offset;
766 fw.kernel_header.kernel_checksum =
767 file->header.checksum;
768 if (strlen(file->header.revision) + strlen(separator) +
769 strlen(file->header.date) >=
770 sizeof(fw.kernel_header.kernel_version))
771 error("Kernel file revision too long\n");
772
773 strcat(fw.kernel_header.kernel_version,
774 file->header.revision);
775 strcat(fw.kernel_header.kernel_version, separator);
776 strcat(fw.kernel_header.kernel_version,
777 file->header.date);
778 } else if (file->header.type == FILE_TYPE_CORE) {
779 core_file = file;
780 }
781
782 fw.header.total_length += file->header.length;
783
784 translate_file_header(&file->header);
785
786 fclose(fp_src);
787 }
788
789 /* update headers with correct lengths and endianness */
790 translate_fw_header(&fw.header);
791
792 write_headers(fp_dst, &fw);
793
794 if (!kernel_offset)
795 error("Kernel image needed for checksum calculation\n");
796
797 /* update headers with correct checksum */
798 fw.header.checksum = htonl(checksum_calculate(fp_dst, kernel_offset));
799 fseek(fp_dst, 0, SEEK_SET);
800 fwrite(&fw.header.checksum, sizeof(fw.header.checksum), 1, fp_dst);
801
802 fw.kernel_header.zld_checksum = fw.header.checksum;
803 strncpy(fw.kernel_header.model_name, fw.header.model_name,
804 sizeof(fw.kernel_header.model_name) - 1);
805 strncpy(fw.kernel_header.capwap_version, fw.header.capwap_version,
806 sizeof(fw.kernel_header.capwap_version) - 1);
807
808 translate_kernel_header(&fw.kernel_header);
809
810 if (core_file) {
811 fw.kernel_header.core_checksum = core_file->header.checksum;
812
813 if (strlen(core_file->header.revision) + strlen(separator) +
814 strlen(core_file->header.date) >=
815 sizeof(fw.kernel_header.core_version))
816 error("Core file revision string too long\n");
817
818 strcat(fw.kernel_header.core_version,
819 core_file->header.revision);
820 strcat(fw.kernel_header.core_version, separator);
821 strcat(fw.kernel_header.core_version, core_file->header.date);
822 }
823
824 fseek(fp_dst, (long int)kernel_offset, SEEK_SET);
825 fwrite(&fw.kernel_header, sizeof(fw.kernel_header), 1, fp_dst);
826
827 fclose(fp_dst);
828
829 exit(0);
830 }