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 43struct seama_seal_header { 44 uint32_t magic; 45 uint16_t reserved; 46 uint16_t metasize; 47 uint32_t imagesize; 48} __attribute__ ((packed)); 49 50struct 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 58char *seama_path; 59int entity_idx = -1; 60char *out_path; 61 62static inline size_t oseama_min(size_t x, size_t y) { 63 return x < y ? x : y; 64} 65 66/************************************************** 67 * Info 68 **************************************************/ 69 70static 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 82static 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 135err_out: 136 return err; 137} 138 139static 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 214err_close: 215 fclose(seama); 216out: 217 return err; 218} 219 220/************************************************** 221 * Create 222 **************************************************/ 223 224static 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 250static 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 266static 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 276static 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 305static 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); 391out: 392 return err; 393} 394 395/************************************************** 396 * Extract 397 **************************************************/ 398 399static 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 414static 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 460static 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 513err_close_out: 514 fclose(out); 515err_close_seama: 516 fclose(seama); 517out: 518 return err; 519} 520 521/************************************************** 522 * Start 523 **************************************************/ 524 525static 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 544int 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} 557