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