OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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 | } |