1/* vi: set sw=4 ts=4: */ 2/* 3 * Copyright (C) 2008, Alpha Networks, Inc. 4 * Created by David Hsieh <david_hsieh@alphanetworks.com> 5 * All right reserved. 6 * 7 * (SEA)ttle i(MA)ge is the image which used in project seattle. 8 * 9 * Redistribution and use in source and binary forms, with or 10 * without modification, are permitted provided that the following 11 * conditions are met: 12 * 13 * 1. Redistributions of source code must retain the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above 18 * copyright notice, this list of conditions and the following 19 * disclaimer in the documentation and/or other materials 20 * provided with the distribution. 21 * 22 * 3. The name of the author may not be used to endorse or promote 23 * products derived from this software without specific prior 24 * written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 28 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 29 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 32 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 34 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 36 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 37 * THE POSSIBILITY OF SUCH DAMAGE. 38 * 39 */ 40 41#include <stdio.h> 42#include <stdint.h> 43#include <stdlib.h> 44#include <stdarg.h> 45#include <sys/types.h> 46#include <sys/stat.h> 47#include <unistd.h> 48#include <string.h> 49#include <arpa/inet.h> 50 51#include "md5.h" 52#include "seama.h" 53 54#define PROGNAME "seama" 55#define VERSION "0.20" 56#define MAX_SEAMA_META_SIZE 1024 57#define MAX_META 128 58#define MAX_IMAGE 128 59 60extern int optind; 61extern char * optarg; 62 63static int o_verbose = 0; /* verbose mode. */ 64static char * o_dump = NULL; /* Seama file to dump. */ 65static char * o_seal = NULL; /* Seal the input images when file name exist. */ 66static char * o_extract = NULL; /* Extract the seama file. */ 67static char * o_images[MAX_IMAGE];/* The image files to pack or seal */ 68static int o_isize = 0; /* number of images */ 69static char * o_meta[MAX_META]; /* meta data array */ 70static int o_msize = 0; /* size of meta array */ 71 72static void verbose(const char * format, ...) 73{ 74 va_list marker; 75 if (o_verbose) 76 { 77 va_start(marker, format); 78 vfprintf(stdout, format, marker); 79 va_end(marker); 80 } 81} 82 83static void cleanup_exit(int exit_code) 84{ 85 verbose("%s: exit with code %d\n", PROGNAME, exit_code); 86 exit(exit_code); 87} 88 89static void show_usage(int exit_code) 90{ 91 printf( PROGNAME " version " VERSION "\n" 92 "usage: " PROGNAME " [OPTIONS]\n" 93 " -h show this help message.\n" 94 " -v verbose mode.\n" 95 " -m {META data} META data.\n" 96 " -d {file} dump the info of the seama file.\n" 97 " -i {input file} image file name.\n" 98 " -s {file} Seal the images to the seama file.\n" 99 " -x {seama file} Extract the seama file.\n" 100 "\n" 101 " SEAMA can pack the input file (with -i) into a seama file.\n" 102 " ex: seama -i target.file\n" 103 " SEAMA can also seal multiple seama files into a single seama file.\n" 104 " ex: seama -s final.file -i taget1.seama -i target2.seama\n" 105 " To extract the raw image from SEAMA, you need to specify the meta.\n" 106 " The first image match the specified meta will be extract to\n" 107 " the output file which was specified with '-x'.\n" 108 " ex: seama -x output -i seama.image -m file=sealpac\n" 109 ); 110 cleanup_exit(exit_code); 111} 112 113static int parse_args(int argc, char * argv[]) 114{ 115 int opt; 116 117 while ((opt = getopt(argc, argv, "hvd:s:i:m:x:")) > 0) 118 { 119 switch (opt) 120 { 121 default: show_usage(-1); break; 122 case 'h': show_usage(0); break; 123 case 'v': o_verbose++; break; 124 case 'd': o_dump = optarg; break; 125 case 's': o_seal = optarg; break; 126 case 'x': o_extract = optarg; break; 127 case 'i': 128 if (o_isize < MAX_IMAGE) o_images[o_isize++] = optarg; 129 else printf("Exceed the maximum acceptable image files.!\n"); 130 break; 131 case 'm': 132 if (o_msize < MAX_META) o_meta[o_msize++] = optarg; 133 else printf("Exceed the maximum acceptable META data.!\n"); 134 break; 135 } 136 } 137 return 0; 138} 139 140/*******************************************************************/ 141 142static size_t calculate_digest(FILE * fh, size_t size, uint8_t * digest) 143{ 144 MD5_CTX ctx; 145 size_t bytes_left, bytes_read, i; 146 uint8_t buf[MAX_SEAMA_META_SIZE]; 147 148 bytes_left = size ? size : sizeof(buf); 149 bytes_read = 0; 150 151 MD5_Init(&ctx); 152 while (!feof(fh) && !ferror(fh) && bytes_left > 0) 153 { 154 i = bytes_left < sizeof(buf) ? bytes_left : sizeof(buf); 155 i = fread(buf, sizeof(char), i, fh); 156 if (i > 0) 157 { 158 MD5_Update(&ctx, buf, i); 159 bytes_read += i; 160 } 161 if (size) bytes_left -= i; 162 } 163 MD5_Final(digest, &ctx); 164 return bytes_read; 165} 166 167#define READ_BUFF_SIZE 8*1024 168static size_t copy_file(FILE * to, FILE * from) 169{ 170 size_t i, fsize = 0; 171 uint8_t buf[READ_BUFF_SIZE]; 172 173 while (!feof(from) && !ferror(from)) 174 { 175 i = fread(buf, sizeof(uint8_t), READ_BUFF_SIZE, from); 176 if (i > 0) 177 { 178 fsize += i; 179 fwrite(buf, sizeof(uint8_t), i, to); 180 } 181 } 182 return fsize; 183} 184 185static int verify_seama(const char * fname, int msg) 186{ 187 FILE * fh = NULL; 188 struct stat st; 189 seamahdr_t shdr; 190 uint8_t checksum[16]; 191 uint8_t digest[16]; 192 uint8_t buf[MAX_SEAMA_META_SIZE]; 193 size_t msize, isize, i; 194 int ret = -1; 195 196#define ERRBREAK(fmt, args...) { if (msg) printf(fmt, ##args); break; } 197 198 do 199 { 200 if (stat(fname, &st) < 0) ERRBREAK("Unable to get the info of '%s'\n",fname); 201 if ((fh = fopen(fname, "r+"))==NULL) ERRBREAK("Unable to open '%s' for reading!\n",fname); 202 203 /* Dump SEAMA header */ 204 if (msg) printf("FILE - %s (%d bytes)\n", fname, (int)st.st_size); 205 206 /* SEAMA */ 207 while (!feof(fh) && !ferror(fh)) 208 { 209 /* read header */ 210 if (fread(&shdr, sizeof(shdr), 1, fh) != 1) break; 211 212 /* Check the magic number */ 213 if (shdr.magic != htonl(SEAMA_MAGIC)) ERRBREAK("Invalid SEAMA magic. Probably no more SEAMA!\n"); 214 215 /* Get the size */ 216 isize = ntohl(shdr.size); 217 msize = ntohs(shdr.metasize); 218 219 /* The checksum exist only if size is greater than zero. */ 220 if (isize > 0) 221 { 222 if (fread(checksum, sizeof(checksum), 1, fh) != 1) 223 ERRBREAK("Error reading checksum !\n"); 224 } 225 226 /* Check the META size. */ 227 if (msize > sizeof(buf)) ERRBREAK("META data in SEAMA header is too large!\n"); 228 229 /* Read META data. */ 230 if (fread(buf, sizeof(char), msize, fh) != msize) 231 ERRBREAK("Unable to read SEAMA META data!\n"); 232 233 /* dump header */ 234 if (msg) 235 { 236 printf("SEAMA ==========================================\n"); 237 printf(" magic : %08x\n", ntohl(shdr.magic)); 238 printf(" meta size : %zu bytes\n", msize); 239 for (i=0; i<msize; i+=(strlen((const char *)&buf[i])+1)) 240 printf(" meta data : %s\n", &buf[i]); 241 printf(" image size : %zu bytes\n", isize); 242 } 243 244 /* verify checksum */ 245 if (isize > 0) 246 { 247 if (msg) 248 { 249 printf(" checksum : "); 250 for (i=0; i<16; i++) printf("%02X", checksum[i]); 251 printf("\n"); 252 } 253 254 /* Calculate the checksum */ 255 calculate_digest(fh, isize, digest); 256 if (msg) 257 { 258 printf(" digest : "); 259 for (i=0; i<16; i++) printf("%02X", digest[i]); 260 printf("\n"); 261 } 262 263 if (memcmp(checksum, digest, 16)!=0) ERRBREAK("!!ERROR!! checksum error !!\n"); 264 ret = 0; 265 } 266 } 267 if (msg) printf("================================================\n"); 268 } while (0); 269 if (fh) fclose(fh); 270 return ret; 271} 272 273static size_t write_seama_header(FILE * fh, char * meta[], size_t msize, size_t size) 274{ 275 seamahdr_t shdr; 276 size_t i; 277 uint16_t metasize = 0; 278 279 /* Calculate the META size */ 280 for (i=0; i<msize; i++) metasize += (strlen(meta[i]) + 1); 281 //+++ let meta data end on 4 alignment by siyou. 2010/3/1 03:58pm 282 metasize = ((metasize+3)/4)*4; 283 verbose("SEAMA META : %d bytes\n", metasize); 284 285 /* Fill up the header, all the data endian should be network byte order. */ 286 shdr.magic = htonl(SEAMA_MAGIC); 287 shdr.reserved = 0; 288 shdr.metasize = htons(metasize); 289 shdr.size = htonl(size); 290 291 /* Write the header */ 292 return fwrite(&shdr, sizeof(seamahdr_t), 1, fh); 293} 294 295static size_t write_checksum(FILE * fh, uint8_t * checksum) 296{ 297 return fwrite(checksum, sizeof(uint8_t), 16, fh); 298} 299 300static size_t write_meta_data(FILE * fh, char * meta[], size_t size) 301{ 302 size_t i,j; 303 size_t ret = 0; 304 305 for (i=0; i<size; i++) 306 { 307 verbose("SEAMA META data : %s\n", meta[i]); 308 j = fwrite(meta[i], sizeof(char), strlen(meta[i])+1, fh); 309 if (j != strlen(meta[i])+1) return 0; 310 ret += j; 311 } 312 //+++ let meta data end on 4 alignment by siyou. 2010/3/1 03:58pm 313 j = ((ret+3)/4)*4; 314 for ( ; ret < j; ret++) 315 fwrite("", sizeof(char), 1, fh); 316 317 return ret; 318} 319 320/*******************************************************************/ 321 322static void dump_seama(const char * fname) 323{ 324 verify_seama(fname, 1); 325} 326 327static void seal_files(const char * file) 328{ 329 FILE * fh; 330 FILE * ifh; 331 size_t i; 332 333 /* Each image should be seama. */ 334 for (i = 0; i < o_isize; i++) 335 { 336 if (verify_seama(o_images[i], 0) < 0) 337 { 338 printf("'%s' is not a seama file !\n",o_images[i]); 339 return; 340 } 341 } 342 343 /* Open file for write */ 344 fh = fopen(file, "w+"); 345 if (fh) 346 { 347 /* Write the header. */ 348 write_seama_header(fh, o_meta, o_msize, 0); 349 write_meta_data(fh, o_meta, o_msize); 350 351 /* Write image files */ 352 for (i=0; i<o_isize; i++) 353 { 354 ifh = fopen(o_images[i], "r+"); 355 if (ifh) 356 { 357 copy_file(fh, ifh); 358 fclose(ifh); 359 } 360 } 361 362 fclose(fh); 363 } 364} 365 366static void pack_files(void) 367{ 368 FILE * fh; 369 FILE * ifh; 370 size_t i, fsize; 371 char filename[512]; 372 uint8_t digest[16]; 373 374 for (i=0; i<o_isize; i++) 375 { 376 /* Open the input file. */ 377 ifh = fopen(o_images[i], "r+"); 378 if (ifh) 379 { 380 fsize = calculate_digest(ifh, 0, digest); 381 verbose("file size (%s) : %d\n", o_images[i], fsize); 382 rewind(ifh); 383 384 /* Open the output file. */ 385 sprintf(filename, "%s.seama", o_images[i]); 386 fh = fopen(filename, "w+"); 387 if (fh) 388 { 389 write_seama_header(fh, o_meta, o_msize, fsize); 390 write_checksum(fh, digest); 391 write_meta_data(fh, o_meta, o_msize); 392 copy_file(fh, ifh); 393 fclose(fh); 394 } 395 fclose(ifh); 396 } 397 else 398 { 399 printf("Unable to open image file '%s'\n",o_images[i]); 400 } 401 } 402} 403 404/**************************************************************************/ 405 406static int match_meta(const char * meta, size_t size) 407{ 408 size_t i, j; 409 int match; 410 411 for (i = 0; i < o_msize; i++) 412 { 413 for (match = 0, j = 0; j < size; j += (strlen(&meta[j])+1)) 414 if (strcmp(&meta[j], o_meta[i])==0) { match++; break; } 415 if (!match) return 0; 416 } 417 return 1; 418} 419 420 421static void extract_file(const char * output) 422{ 423 FILE * ifh = NULL; 424 FILE * ofh = NULL; 425 size_t msize, isize, i, m; 426 seamahdr_t shdr; 427 uint8_t buf[MAX_SEAMA_META_SIZE]; 428 int done = 0; 429 430 /* We need meta for searching the target image. */ 431 if (o_msize == 0) 432 { 433 printf("SEAMA: need meta for searching image.\n"); 434 return; 435 } 436 437 /* Walk through each input file */ 438 for (i = 0; i < o_isize; i++) 439 { 440 /* verify the input file */ 441 if (verify_seama(o_images[i], 0) < 0) 442 { 443 printf("SEAMA: '%s' is not a seama file !\n", o_images[i]); 444 continue; 445 } 446 /* open the input file */ 447 ifh = fopen(o_images[i], "r"); 448 if (!ifh) continue; 449 /* read file */ 450 while (!feof(ifh) && !ferror(ifh)) 451 { 452 /* read header */ 453 fread(&shdr, sizeof(shdr), 1, ifh); 454 if (shdr.magic != htonl(SEAMA_MAGIC)) break; 455 /* Get the size */ 456 isize = ntohl(shdr.size); 457 msize = ntohs(shdr.metasize); 458 if (isize == 0) 459 { 460 while (msize > 0) 461 { 462 m = fread(buf, sizeof(char), (msize < MAX_SEAMA_META_SIZE) ? msize : MAX_SEAMA_META_SIZE, ifh); 463 if (m <= 0) break; 464 msize -= m; 465 } 466 continue; 467 } 468 /* read checksum */ 469 fread(buf, sizeof(char), 16, ifh); 470 if (msize > 0) 471 { 472 /* read META */ 473 fread(buf, sizeof(char), msize, ifh); 474 if (match_meta((const char *)buf, msize)) 475 { 476 printf("SEAMA: found image @ '%s', image size: %zu\n", o_images[i], isize); 477 /* open output file */ 478 ofh = fopen(output, "w"); 479 if (!ofh) printf("SEAMA: unable to open '%s' for writting.\n",output); 480 else 481 { 482 while (isize > 0) 483 { 484 m = fread(buf, sizeof(char), (isize < MAX_SEAMA_META_SIZE) ? isize : MAX_SEAMA_META_SIZE, ifh); 485 if (m <= 0) break; 486 fwrite(buf, sizeof(char), m, ofh); 487 isize -= m; 488 } 489 fclose(ofh); 490 } 491 done++; 492 break; 493 } 494 } 495 while (isize > 0) 496 { 497 m = fread(buf, sizeof(char), (isize < MAX_SEAMA_META_SIZE) ? isize : MAX_SEAMA_META_SIZE, ifh); 498 if (m <= 0) break; 499 isize -= m; 500 } 501 } 502 /* close the file. */ 503 fclose(ifh); 504 if (done) break; 505 } 506 return; 507} 508 509/*******************************************************************/ 510#ifdef RGBIN_BOX 511int seama_main(int argc, char * argv[], char * env[]) 512#else 513int main(int argc, char * argv[], char * env[]) 514#endif 515{ 516 verbose("SEAMA version " VERSION "\n"); 517 518 /* parse the arguments */ 519 if (parse_args(argc, argv) < 0) show_usage(9); 520 521 /* Do the works */ 522 if (o_dump) dump_seama(o_dump); 523 else if (o_seal) seal_files(o_seal); 524 else if (o_extract) extract_file(o_extract); 525 else pack_files(); 526 527 cleanup_exit(0); 528 return 0; 529} 530