firmware-utils: add otrx tool for handling TRX images
[openwrt/openwrt.git] / tools / firmware-utils / src / oseama.c
1 /*
2 * oseama
3 *
4 * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 */
11
12 #include <byteswap.h>
13 #include <endian.h>
14 #include <errno.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #include "md5.h"
22
23 #if !defined(__BYTE_ORDER)
24 #error "Unknown byte order"
25 #endif
26
27 #if __BYTE_ORDER == __BIG_ENDIAN
28 #define cpu_to_be32(x) (x)
29 #define be32_to_cpu(x) (x)
30 #define cpu_to_be16(x) (x)
31 #define be16_to_cpu(x) (x)
32 #elif __BYTE_ORDER == __LITTLE_ENDIAN
33 #define cpu_to_be32(x) bswap_32(x)
34 #define be32_to_cpu(x) bswap_32(x)
35 #define cpu_to_be16(x) bswap_16(x)
36 #define be16_to_cpu(x) bswap_16(x)
37 #else
38 #error "Unsupported endianness"
39 #endif
40
41 #define SEAMA_MAGIC 0x5ea3a417
42
43 struct seama_seal_header {
44 uint32_t magic;
45 uint16_t reserved;
46 uint16_t metasize;
47 uint32_t imagesize;
48 } __attribute__ ((packed));
49
50 struct seama_entity_header {
51 uint32_t magic;
52 uint16_t reserved;
53 uint16_t metasize;
54 uint32_t imagesize;
55 uint8_t md5[16];
56 } __attribute__ ((packed));
57
58 char *seama_path;
59 int entity_idx = -1;
60 char *out_path;
61
62 static inline size_t oseama_min(size_t x, size_t y) {
63 return x < y ? x : y;
64 }
65
66 /**************************************************
67 * Info
68 **************************************************/
69
70 static void oseama_info_parse_options(int argc, char **argv) {
71 int c;
72
73 while ((c = getopt(argc, argv, "e:")) != -1) {
74 switch (c) {
75 case 'e':
76 entity_idx = atoi(optarg);
77 break;
78 }
79 }
80 }
81
82 static int oseama_info_entities(FILE *seama) {
83 struct seama_entity_header hdr;
84 size_t bytes, metasize, imagesize;
85 uint8_t buf[1024];
86 char *end, *tmp;
87 int i = 0;
88 int err = 0;
89
90 while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) {
91 if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
92 fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
93 err = -EINVAL;
94 goto err_out;
95 }
96 metasize = be16_to_cpu(hdr.metasize);
97 imagesize = be32_to_cpu(hdr.imagesize);
98
99 if (entity_idx >= 0 && i != entity_idx) {
100 fseek(seama, metasize + imagesize, SEEK_CUR);
101 i++;
102 continue;
103 }
104
105 if (metasize >= sizeof(buf)) {
106 fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%zd B)\n", sizeof(buf), metasize);
107 err = -EINVAL;
108 goto err_out;
109 }
110
111 if (entity_idx < 0)
112 printf("\n");
113 printf("Entity offset:\t%ld\n", ftell(seama) - sizeof(hdr));
114 printf("Entity size:\t%zd\n", sizeof(hdr) + metasize + imagesize);
115 printf("Meta size:\t%zd\n", metasize);
116 printf("Image size:\t%zd\n", imagesize);
117
118 bytes = fread(buf, 1, metasize, seama);
119 if (bytes != metasize) {
120 fprintf(stderr, "Couldn't read %zd B of meta\n", metasize);
121 err = -EIO;
122 goto err_out;
123 }
124
125 end = (char *)&buf[metasize - 1];
126 *end = '\0';
127 for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
128 printf("Meta entry:\t%s\n", tmp);
129 }
130
131 fseek(seama, imagesize, SEEK_CUR);
132 i++;
133 }
134
135 err_out:
136 return err;
137 }
138
139 static int oseama_info(int argc, char **argv) {
140 FILE *seama;
141 struct seama_seal_header hdr;
142 size_t bytes;
143 uint16_t metasize;
144 uint32_t imagesize;
145 uint8_t buf[1024];
146 int err = 0;
147
148 if (argc < 3) {
149 fprintf(stderr, "No Seama file passed\n");
150 err = -EINVAL;
151 goto out;
152 }
153 seama_path = argv[2];
154
155 optind = 3;
156 oseama_info_parse_options(argc, argv);
157
158 seama = fopen(seama_path, "r");
159 if (!seama) {
160 fprintf(stderr, "Couldn't open %s\n", seama_path);
161 err = -EACCES;
162 goto out;
163 }
164
165 bytes = fread(&hdr, 1, sizeof(hdr), seama);
166 if (bytes != sizeof(hdr)) {
167 fprintf(stderr, "Couldn't read %s header\n", seama_path);
168 err = -EIO;
169 goto err_close;
170 }
171 metasize = be16_to_cpu(hdr.metasize);
172 imagesize = be32_to_cpu(hdr.imagesize);
173
174 if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
175 fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
176 err = -EINVAL;
177 goto err_close;
178 }
179
180 if (metasize >= sizeof(buf)) {
181 fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%d B)\n", sizeof(buf), metasize);
182 err = -EINVAL;
183 goto err_close;
184 }
185
186 if (imagesize) {
187 fprintf(stderr, "Invalid Seama image size: 0x%08x (should be 0)\n", imagesize);
188 err = -EINVAL;
189 goto err_close;
190 }
191
192 bytes = fread(buf, 1, metasize, seama);
193 if (bytes != metasize) {
194 fprintf(stderr, "Couldn't read %d B of meta\n", metasize);
195 err = -EIO;
196 goto err_close;
197 }
198
199 if (entity_idx < 0) {
200 char *end, *tmp;
201
202 printf("Meta size:\t%d\n", metasize);
203 printf("Image size:\t%d\n", imagesize);
204
205 end = (char *)&buf[metasize - 1];
206 *end = '\0';
207 for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
208 printf("Meta entry:\t%s\n", tmp);
209 }
210 }
211
212 oseama_info_entities(seama);
213
214 err_close:
215 fclose(seama);
216 out:
217 return err;
218 }
219
220 /**************************************************
221 * Create
222 **************************************************/
223
224 static ssize_t oseama_entity_append_file(FILE *seama, const char *in_path) {
225 FILE *in;
226 size_t bytes;
227 ssize_t length = 0;
228 uint8_t buf[128];
229
230 in = fopen(in_path, "r");
231 if (!in) {
232 fprintf(stderr, "Couldn't open %s\n", in_path);
233 return -EACCES;
234 }
235
236 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
237 if (fwrite(buf, 1, bytes, seama) != bytes) {
238 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, seama_path);
239 length = -EIO;
240 break;
241 }
242 length += bytes;
243 }
244
245 fclose(in);
246
247 return length;
248 }
249
250 static ssize_t oseama_entity_append_zeros(FILE *seama, size_t length) {
251 uint8_t *buf;
252
253 buf = malloc(length);
254 if (!buf)
255 return -ENOMEM;
256 memset(buf, 0, length);
257
258 if (fwrite(buf, 1, length, seama) != length) {
259 fprintf(stderr, "Couldn't write %zu B to %s\n", length, seama_path);
260 return -EIO;
261 }
262
263 return length;
264 }
265
266 static ssize_t oseama_entity_align(FILE *seama, size_t curr_offset, size_t alignment) {
267 if (curr_offset & (alignment - 1)) {
268 size_t length = alignment - (curr_offset % alignment);
269
270 return oseama_entity_append_zeros(seama, length);
271 }
272
273 return 0;
274 }
275
276 static int oseama_entity_write_hdr(FILE *seama, size_t metasize, size_t imagesize) {
277 struct seama_entity_header hdr = {};
278 uint8_t buf[128];
279 size_t length = imagesize;
280 size_t bytes;
281 MD5_CTX ctx;
282
283 fseek(seama, sizeof(hdr) + metasize, SEEK_SET);
284 MD5_Init(&ctx);
285 while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) {
286 MD5_Update(&ctx, buf, bytes);
287 length -= bytes;
288 }
289 MD5_Final(hdr.md5, &ctx);
290
291 hdr.magic = cpu_to_be32(SEAMA_MAGIC);
292 hdr.metasize = cpu_to_be16(metasize);
293 hdr.imagesize = cpu_to_be32(imagesize);
294
295 fseek(seama, 0, SEEK_SET);
296 bytes = fwrite(&hdr, 1, sizeof(hdr), seama);
297 if (bytes != sizeof(hdr)) {
298 fprintf(stderr, "Couldn't write Seama entity header to %s\n", seama_path);
299 return -EIO;
300 }
301
302 return 0;
303 }
304
305 static int oseama_entity(int argc, char **argv) {
306 FILE *seama;
307 ssize_t sbytes;
308 size_t curr_offset = sizeof(struct seama_entity_header);
309 size_t metasize = 0, imagesize = 0;
310 int c;
311 int err = 0;
312
313 if (argc < 3) {
314 fprintf(stderr, "No Seama file passed\n");
315 err = -EINVAL;
316 goto out;
317 }
318 seama_path = argv[2];
319
320 seama = fopen(seama_path, "w+");
321 if (!seama) {
322 fprintf(stderr, "Couldn't open %s\n", seama_path);
323 err = -EACCES;
324 goto out;
325 }
326 fseek(seama, curr_offset, SEEK_SET);
327
328 optind = 3;
329 while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
330 switch (c) {
331 case 'm':
332 sbytes = fwrite(optarg, 1, strlen(optarg) + 1, seama);
333 if (sbytes < 0) {
334 fprintf(stderr, "Failed to write meta %s\n", optarg);
335 } else {
336 curr_offset += sbytes;
337 metasize += sbytes;
338 }
339
340 sbytes = oseama_entity_align(seama, curr_offset, 4);
341 if (sbytes < 0) {
342 fprintf(stderr, "Failed to append zeros\n");
343 } else {
344 curr_offset += sbytes;
345 metasize += sbytes;
346 }
347
348 break;
349 case 'f':
350 case 'b':
351 break;
352 }
353 }
354
355 optind = 3;
356 while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
357 switch (c) {
358 case 'm':
359 break;
360 case 'f':
361 sbytes = oseama_entity_append_file(seama, optarg);
362 if (sbytes < 0) {
363 fprintf(stderr, "Failed to append file %s\n", optarg);
364 } else {
365 curr_offset += sbytes;
366 imagesize += sbytes;
367 }
368 break;
369 case 'b':
370 sbytes = strtol(optarg, NULL, 0) - curr_offset;
371 if (sbytes < 0) {
372 fprintf(stderr, "Current Seama entity length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
373 } else {
374 sbytes = oseama_entity_append_zeros(seama, sbytes);
375 if (sbytes < 0) {
376 fprintf(stderr, "Failed to append zeros\n");
377 } else {
378 curr_offset += sbytes;
379 imagesize += sbytes;
380 }
381 }
382 break;
383 }
384 if (err)
385 break;
386 }
387
388 oseama_entity_write_hdr(seama, metasize, imagesize);
389
390 fclose(seama);
391 out:
392 return err;
393 }
394
395 /**************************************************
396 * Extract
397 **************************************************/
398
399 static void oseama_extract_parse_options(int argc, char **argv) {
400 int c;
401
402 while ((c = getopt(argc, argv, "e:o:")) != -1) {
403 switch (c) {
404 case 'e':
405 entity_idx = atoi(optarg);
406 break;
407 case 'o':
408 out_path = optarg;
409 break;
410 }
411 }
412 }
413
414 static int oseama_extract_entity(FILE *seama, FILE *out) {
415 struct seama_entity_header hdr;
416 size_t bytes, metasize, imagesize, length;
417 uint8_t buf[1024];
418 int i = 0;
419 int err = 0;
420
421 while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) {
422 if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
423 fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
424 err = -EINVAL;
425 break;
426 }
427 metasize = be16_to_cpu(hdr.metasize);
428 imagesize = be32_to_cpu(hdr.imagesize);
429
430 if (i != entity_idx) {
431 fseek(seama, metasize + imagesize, SEEK_CUR);
432 i++;
433 continue;
434 }
435
436 fseek(seama, -sizeof(hdr), SEEK_CUR);
437
438 length = sizeof(hdr) + metasize + imagesize;
439 while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) {
440 if (fwrite(buf, 1, bytes, out) != bytes) {
441 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
442 err = -EIO;
443 break;
444 }
445 length -= bytes;
446 }
447
448 if (length) {
449 fprintf(stderr, "Couldn't extract whole entity %d from %s (%zu B left)\n", entity_idx, seama_path, length);
450 err = -EIO;
451 break;
452 }
453
454 break;
455 }
456
457 return err;
458 }
459
460 static int oseama_extract(int argc, char **argv) {
461 FILE *seama;
462 FILE *out;
463 struct seama_seal_header hdr;
464 size_t bytes;
465 uint16_t metasize;
466 int err = 0;
467
468 if (argc < 3) {
469 fprintf(stderr, "No Seama file passed\n");
470 err = -EINVAL;
471 goto out;
472 }
473 seama_path = argv[2];
474
475 optind = 3;
476 oseama_extract_parse_options(argc, argv);
477 if (entity_idx < 0) {
478 fprintf(stderr, "No entity specified\n");
479 err = -EINVAL;
480 goto out;
481 } else if (!out_path) {
482 fprintf(stderr, "No output file specified\n");
483 err = -EINVAL;
484 goto out;
485 }
486
487 seama = fopen(seama_path, "r");
488 if (!seama) {
489 fprintf(stderr, "Couldn't open %s\n", seama_path);
490 err = -EACCES;
491 goto out;
492 }
493
494 out = fopen(out_path, "w");
495 if (!out) {
496 fprintf(stderr, "Couldn't open %s\n", out_path);
497 err = -EACCES;
498 goto err_close_seama;
499 }
500
501 bytes = fread(&hdr, 1, sizeof(hdr), seama);
502 if (bytes != sizeof(hdr)) {
503 fprintf(stderr, "Couldn't read %s header\n", seama_path);
504 err = -EIO;
505 goto err_close_out;
506 }
507 metasize = be16_to_cpu(hdr.metasize);
508
509 fseek(seama, metasize, SEEK_CUR);
510
511 oseama_extract_entity(seama, out);
512
513 err_close_out:
514 fclose(out);
515 err_close_seama:
516 fclose(seama);
517 out:
518 return err;
519 }
520
521 /**************************************************
522 * Start
523 **************************************************/
524
525 static void usage() {
526 printf("Usage:\n");
527 printf("\n");
528 printf("Info about Seama seal (container):\n");
529 printf("\toseama info <file> [options]\n");
530 printf("\t-e\t\t\t\tprint info about specified entity only\n");
531 printf("\n");
532 printf("Create Seama entity:\n");
533 printf("\toseama entity <file> [options]\n");
534 printf("\t-m meta\t\t\t\tmeta into to put in header\n");
535 printf("\t-f file\t\t\t\tappend content from file\n");
536 printf("\t-b offset\t\t\tappend zeros till reaching absolute offset\n");
537 printf("\n");
538 printf("Extract from Seama seal (container):\n");
539 printf("\toseama extract <file> [options]\n");
540 printf("\t-e\t\t\t\tindex of entity to extract\n");
541 printf("\t-o file\t\t\t\toutput file\n");
542 }
543
544 int main(int argc, char **argv) {
545 if (argc > 1) {
546 if (!strcmp(argv[1], "info"))
547 return oseama_info(argc, argv);
548 else if (!strcmp(argv[1], "entity"))
549 return oseama_entity(argc, argv);
550 else if (!strcmp(argv[1], "extract"))
551 return oseama_extract(argc, argv);
552 }
553
554 usage();
555 return 0;
556 }