ptgen: add Chromium OS kernel partition support
[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 * Helpers
64 **************************************************/
65
66 static FILE *oseama_open(const char *pathname, const char *mode) {
67 if (strcmp(pathname, "-"))
68 return fopen(pathname, mode);
69
70 if (isatty(fileno(stdin))) {
71 fprintf(stderr, "Reading from TTY stdin is unsupported\n");
72 return NULL;
73 }
74
75 return stdin;
76 }
77
78 static int oseama_skip(FILE *fp, size_t length)
79 {
80 if (fseek(fp, length, SEEK_CUR)) {
81 uint8_t buf[1024];
82 size_t bytes;
83
84 do {
85 bytes = fread(buf, 1, oseama_min(sizeof(buf), length), fp);
86 if (bytes <= 0)
87 return -EIO;
88 length -= bytes;
89 } while (length);
90 }
91
92 return 0;
93 }
94
95 static void oseama_close(FILE *fp) {
96 if (fp != stdin)
97 fclose(fp);
98 }
99
100 /**************************************************
101 * Info
102 **************************************************/
103
104 static void oseama_info_parse_options(int argc, char **argv) {
105 int c;
106
107 while ((c = getopt(argc, argv, "e:")) != -1) {
108 switch (c) {
109 case 'e':
110 entity_idx = atoi(optarg);
111 break;
112 }
113 }
114 }
115
116 static int oseama_info_entities(FILE *seama, size_t *pos) {
117 struct seama_entity_header hdr;
118 size_t bytes, metasize, imagesize;
119 uint8_t buf[1024];
120 char *end, *tmp;
121 int i = 0;
122 int err = 0;
123
124 while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) {
125 *pos += bytes;
126
127 if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
128 fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
129 err = -EINVAL;
130 goto err_out;
131 }
132 metasize = be16_to_cpu(hdr.metasize);
133 imagesize = be32_to_cpu(hdr.imagesize);
134
135 if (entity_idx >= 0 && i != entity_idx) {
136 oseama_skip(seama, metasize + imagesize);
137 *pos += metasize + imagesize;
138 i++;
139 continue;
140 }
141
142 if (metasize >= sizeof(buf)) {
143 fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%zd B)\n", sizeof(buf), metasize);
144 err = -EINVAL;
145 goto err_out;
146 }
147
148 if (entity_idx < 0)
149 printf("\n");
150 printf("Entity offset:\t%ld\n", *pos - sizeof(hdr));
151 printf("Entity size:\t%zd\n", sizeof(hdr) + metasize + imagesize);
152 printf("Meta size:\t%zd\n", metasize);
153 printf("Image size:\t%zd\n", imagesize);
154
155 bytes = fread(buf, 1, metasize, seama);
156 if (bytes != metasize) {
157 fprintf(stderr, "Couldn't read %zd B of meta\n", metasize);
158 err = -EIO;
159 goto err_out;
160 }
161 *pos += bytes;
162
163 end = (char *)&buf[metasize - 1];
164 *end = '\0';
165 for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
166 printf("Meta entry:\t%s\n", tmp);
167 }
168
169 oseama_skip(seama, imagesize);
170 *pos += imagesize;
171 i++;
172 }
173
174 err_out:
175 return err;
176 }
177
178 static int oseama_info(int argc, char **argv) {
179 FILE *seama;
180 struct seama_seal_header hdr;
181 size_t bytes;
182 uint16_t metasize;
183 uint32_t imagesize;
184 uint8_t buf[1024];
185 size_t pos = 0;
186 int err = 0;
187
188 if (argc < 3) {
189 fprintf(stderr, "No Seama file passed\n");
190 err = -EINVAL;
191 goto out;
192 }
193 seama_path = argv[2];
194
195 optind = 3;
196 oseama_info_parse_options(argc, argv);
197
198 seama = oseama_open(seama_path, "r");
199 if (!seama) {
200 fprintf(stderr, "Couldn't open %s\n", seama_path);
201 err = -EACCES;
202 goto out;
203 }
204
205 bytes = fread(&hdr, 1, sizeof(hdr), seama);
206 if (bytes != sizeof(hdr)) {
207 fprintf(stderr, "Couldn't read %s header\n", seama_path);
208 err = -EIO;
209 goto err_close;
210 }
211 pos += bytes;
212
213 metasize = be16_to_cpu(hdr.metasize);
214 imagesize = be32_to_cpu(hdr.imagesize);
215
216 if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
217 fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
218 err = -EINVAL;
219 goto err_close;
220 }
221
222 if (metasize >= sizeof(buf)) {
223 fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%d B)\n", sizeof(buf), metasize);
224 err = -EINVAL;
225 goto err_close;
226 }
227
228 if (imagesize) {
229 fprintf(stderr, "Invalid Seama image size: 0x%08x (should be 0)\n", imagesize);
230 err = -EINVAL;
231 goto err_close;
232 }
233
234 bytes = fread(buf, 1, metasize, seama);
235 if (bytes != metasize) {
236 fprintf(stderr, "Couldn't read %d B of meta\n", metasize);
237 err = -EIO;
238 goto err_close;
239 }
240 pos += bytes;
241
242 if (entity_idx < 0) {
243 char *end, *tmp;
244
245 printf("Meta size:\t%d\n", metasize);
246 printf("Image size:\t%d\n", imagesize);
247
248 end = (char *)&buf[metasize - 1];
249 *end = '\0';
250 for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) {
251 printf("Meta entry:\t%s\n", tmp);
252 }
253 }
254
255 oseama_info_entities(seama, &pos);
256
257 err_close:
258 oseama_close(seama);
259 out:
260 return err;
261 }
262
263 /**************************************************
264 * Create
265 **************************************************/
266
267 static ssize_t oseama_entity_append_file(FILE *seama, const char *in_path) {
268 FILE *in;
269 size_t bytes;
270 ssize_t length = 0;
271 uint8_t buf[128];
272
273 in = fopen(in_path, "r");
274 if (!in) {
275 fprintf(stderr, "Couldn't open %s\n", in_path);
276 return -EACCES;
277 }
278
279 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
280 if (fwrite(buf, 1, bytes, seama) != bytes) {
281 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, seama_path);
282 length = -EIO;
283 break;
284 }
285 length += bytes;
286 }
287
288 fclose(in);
289
290 return length;
291 }
292
293 static ssize_t oseama_entity_append_zeros(FILE *seama, size_t length) {
294 uint8_t *buf;
295
296 buf = malloc(length);
297 if (!buf)
298 return -ENOMEM;
299 memset(buf, 0, length);
300
301 if (fwrite(buf, 1, length, seama) != length) {
302 fprintf(stderr, "Couldn't write %zu B to %s\n", length, seama_path);
303 return -EIO;
304 }
305
306 return length;
307 }
308
309 static ssize_t oseama_entity_align(FILE *seama, size_t curr_offset, size_t alignment) {
310 if (curr_offset & (alignment - 1)) {
311 size_t length = alignment - (curr_offset % alignment);
312
313 return oseama_entity_append_zeros(seama, length);
314 }
315
316 return 0;
317 }
318
319 static int oseama_entity_write_hdr(FILE *seama, size_t metasize, size_t imagesize) {
320 struct seama_entity_header hdr = {};
321 uint8_t buf[128];
322 size_t length = imagesize;
323 size_t bytes;
324 MD5_CTX ctx;
325
326 fseek(seama, sizeof(hdr) + metasize, SEEK_SET);
327 MD5_Init(&ctx);
328 while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) {
329 MD5_Update(&ctx, buf, bytes);
330 length -= bytes;
331 }
332 MD5_Final(hdr.md5, &ctx);
333
334 hdr.magic = cpu_to_be32(SEAMA_MAGIC);
335 hdr.metasize = cpu_to_be16(metasize);
336 hdr.imagesize = cpu_to_be32(imagesize);
337
338 fseek(seama, 0, SEEK_SET);
339 bytes = fwrite(&hdr, 1, sizeof(hdr), seama);
340 if (bytes != sizeof(hdr)) {
341 fprintf(stderr, "Couldn't write Seama entity header to %s\n", seama_path);
342 return -EIO;
343 }
344
345 return 0;
346 }
347
348 static int oseama_entity(int argc, char **argv) {
349 FILE *seama;
350 ssize_t sbytes;
351 size_t curr_offset = sizeof(struct seama_entity_header);
352 size_t metasize = 0, imagesize = 0;
353 int c;
354 int err = 0;
355
356 if (argc < 3) {
357 fprintf(stderr, "No Seama file passed\n");
358 err = -EINVAL;
359 goto out;
360 }
361 seama_path = argv[2];
362
363 seama = fopen(seama_path, "w+");
364 if (!seama) {
365 fprintf(stderr, "Couldn't open %s\n", seama_path);
366 err = -EACCES;
367 goto out;
368 }
369 fseek(seama, curr_offset, SEEK_SET);
370
371 optind = 3;
372 while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
373 switch (c) {
374 case 'm':
375 sbytes = fwrite(optarg, 1, strlen(optarg) + 1, seama);
376 if (sbytes < 0) {
377 fprintf(stderr, "Failed to write meta %s\n", optarg);
378 } else {
379 curr_offset += sbytes;
380 metasize += sbytes;
381 }
382
383 sbytes = oseama_entity_align(seama, curr_offset, 4);
384 if (sbytes < 0) {
385 fprintf(stderr, "Failed to append zeros\n");
386 } else {
387 curr_offset += sbytes;
388 metasize += sbytes;
389 }
390
391 break;
392 case 'f':
393 case 'b':
394 break;
395 }
396 }
397
398 optind = 3;
399 while ((c = getopt(argc, argv, "m:f:b:")) != -1) {
400 switch (c) {
401 case 'm':
402 break;
403 case 'f':
404 sbytes = oseama_entity_append_file(seama, optarg);
405 if (sbytes < 0) {
406 fprintf(stderr, "Failed to append file %s\n", optarg);
407 } else {
408 curr_offset += sbytes;
409 imagesize += sbytes;
410 }
411 break;
412 case 'b':
413 sbytes = strtol(optarg, NULL, 0) - curr_offset;
414 if (sbytes < 0) {
415 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));
416 } else {
417 sbytes = oseama_entity_append_zeros(seama, sbytes);
418 if (sbytes < 0) {
419 fprintf(stderr, "Failed to append zeros\n");
420 } else {
421 curr_offset += sbytes;
422 imagesize += sbytes;
423 }
424 }
425 break;
426 }
427 if (err)
428 break;
429 }
430
431 oseama_entity_write_hdr(seama, metasize, imagesize);
432
433 fclose(seama);
434 out:
435 return err;
436 }
437
438 /**************************************************
439 * Extract
440 **************************************************/
441
442 static void oseama_extract_parse_options(int argc, char **argv) {
443 int c;
444
445 while ((c = getopt(argc, argv, "e:o:")) != -1) {
446 switch (c) {
447 case 'e':
448 entity_idx = atoi(optarg);
449 break;
450 case 'o':
451 out_path = optarg;
452 break;
453 }
454 }
455 }
456
457 static int oseama_extract_entity(FILE *seama, FILE *out) {
458 struct seama_entity_header hdr;
459 size_t bytes, metasize, imagesize, length;
460 uint8_t buf[1024];
461 int i = 0;
462 int err = 0;
463
464 while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) {
465 if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) {
466 fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic));
467 err = -EINVAL;
468 break;
469 }
470 metasize = be16_to_cpu(hdr.metasize);
471 imagesize = be32_to_cpu(hdr.imagesize);
472
473 if (i != entity_idx) {
474 oseama_skip(seama, metasize + imagesize);
475 i++;
476 continue;
477 }
478
479 if (fwrite(&hdr, 1, sizeof(hdr), out) != sizeof(hdr)) {
480 fprintf(stderr, "Couldn't write %zu B to %s\n", sizeof(hdr), out_path);
481 err = -EIO;
482 break;
483 }
484
485 length = metasize + imagesize;
486 while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) {
487 if (fwrite(buf, 1, bytes, out) != bytes) {
488 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
489 err = -EIO;
490 break;
491 }
492 length -= bytes;
493 }
494
495 if (length) {
496 fprintf(stderr, "Couldn't extract whole entity %d from %s (%zu B left)\n", entity_idx, seama_path, length);
497 err = -EIO;
498 break;
499 }
500
501 break;
502 }
503
504 return err;
505 }
506
507 static int oseama_extract(int argc, char **argv) {
508 FILE *seama;
509 FILE *out;
510 struct seama_seal_header hdr;
511 size_t bytes;
512 uint16_t metasize;
513 int err = 0;
514
515 if (argc < 3) {
516 fprintf(stderr, "No Seama file passed\n");
517 err = -EINVAL;
518 goto out;
519 }
520 seama_path = argv[2];
521
522 optind = 3;
523 oseama_extract_parse_options(argc, argv);
524 if (entity_idx < 0) {
525 fprintf(stderr, "No entity specified\n");
526 err = -EINVAL;
527 goto out;
528 }
529
530 seama = oseama_open(seama_path, "r");
531 if (!seama) {
532 fprintf(stderr, "Couldn't open %s\n", seama_path);
533 err = -EACCES;
534 goto out;
535 }
536
537 if (out_path) {
538 out = fopen(out_path, "w");
539 if (!out) {
540 fprintf(stderr, "Couldn't open %s\n", out_path);
541 err = -EACCES;
542 goto err_close_seama;
543 }
544 } else {
545 out = stdout;
546 }
547
548 bytes = fread(&hdr, 1, sizeof(hdr), seama);
549 if (bytes != sizeof(hdr)) {
550 fprintf(stderr, "Couldn't read %s header\n", seama_path);
551 err = -EIO;
552 goto err_close_out;
553 }
554 metasize = be16_to_cpu(hdr.metasize);
555
556 oseama_skip(seama, metasize);
557
558 oseama_extract_entity(seama, out);
559
560 err_close_out:
561 if (out != stdout)
562 fclose(out);
563 err_close_seama:
564 oseama_close(seama);
565 out:
566 return err;
567 }
568
569 /**************************************************
570 * Start
571 **************************************************/
572
573 static void usage() {
574 printf("Usage:\n");
575 printf("\n");
576 printf("Info about Seama seal (container):\n");
577 printf("\toseama info <file> [options]\n");
578 printf("\t-e\t\t\t\tprint info about specified entity only\n");
579 printf("\n");
580 printf("Create Seama entity:\n");
581 printf("\toseama entity <file> [options]\n");
582 printf("\t-m meta\t\t\t\tmeta into to put in header\n");
583 printf("\t-f file\t\t\t\tappend content from file\n");
584 printf("\t-b offset\t\t\tappend zeros till reaching absolute offset\n");
585 printf("\n");
586 printf("Extract from Seama seal (container):\n");
587 printf("\toseama extract <file> [options]\n");
588 printf("\t-e\t\t\t\tindex of entity to extract\n");
589 printf("\t-o file\t\t\t\toutput file\n");
590 }
591
592 int main(int argc, char **argv) {
593 if (argc > 1) {
594 if (!strcmp(argv[1], "info"))
595 return oseama_info(argc, argv);
596 else if (!strcmp(argv[1], "entity"))
597 return oseama_entity(argc, argv);
598 else if (!strcmp(argv[1], "extract"))
599 return oseama_extract(argc, argv);
600 }
601
602 usage();
603 return 0;
604 }