mkyaffs2image.c revision 84f79e8e767a95ea761f430a12adb13ce687ae7e
1/* 2 * YAFFS: Yet another FFS. A NAND-flash specific file system. 3 * 4 * makeyaffsimage.c 5 * 6 * Makes a YAFFS file system image that can be used to load up a file system. 7 * 8 * Copyright (C) 2002 Aleph One Ltd. 9 * for Toby Churchill Ltd and Brightstar Engineering 10 * 11 * Created by Charles Manning <charles@aleph1.co.uk> 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License version 2 as 15 * published by the Free Software Foundation. 16 * 17 * 18 * Nick Bane modifications flagged NCB 19 * 20 * Endian handling patches by James Ng. 21 * 22 * mkyaffs2image hacks by NCB 23 * 24 */ 25 26#include <stdlib.h> 27#include <stdio.h> 28#include <fcntl.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <dirent.h> 32#include <string.h> 33#include <unistd.h> 34 35#ifdef HAVE_SELINUX 36#define XATTR_NAME_SELINUX "security.selinux" 37#include <selinux/selinux.h> 38#include <selinux/label.h> 39 40static struct selabel_handle *sehnd; 41static unsigned int seprefixlen; 42static char *mntpoint; 43#endif 44 45#include <private/android_filesystem_config.h> 46 47#include "yaffs_ecc.h" 48#include "yaffs_guts.h" 49 50#include "yaffs_tagsvalidity.h" 51#include "yaffs_packedtags2.h" 52 53unsigned source_path_len = 0; 54unsigned yaffs_traceMask=0; 55 56#define MAX_OBJECTS 50000 57 58unsigned chunkSize = 2048; 59unsigned spareSize = 64; 60 61const char * mkyaffsimage_c_version = "$Id: mkyaffs2image.c,v 1.2 2005/12/13 00:34:58 tpoynor Exp $"; 62 63 64typedef struct 65{ 66 dev_t dev; 67 ino_t ino; 68 int obj; 69} objItem; 70 71 72static objItem obj_list[MAX_OBJECTS]; 73static int n_obj = 0; 74static int obj_id = YAFFS_NOBJECT_BUCKETS + 1; 75 76static int nObjects, nDirectories, nPages; 77 78static int outFile; 79 80static int error; 81 82#ifdef HAVE_BIG_ENDIAN 83static int convert_endian = 1; 84#elif defined(HAVE_LITTLE_ENDIAN) 85static int convert_endian = 0; 86#endif 87 88static int obj_compare(const void *a, const void * b) 89{ 90 objItem *oa, *ob; 91 92 oa = (objItem *)a; 93 ob = (objItem *)b; 94 95 if(oa->dev < ob->dev) return -1; 96 if(oa->dev > ob->dev) return 1; 97 if(oa->ino < ob->ino) return -1; 98 if(oa->ino > ob->ino) return 1; 99 100 return 0; 101} 102 103 104static void add_obj_to_list(dev_t dev, ino_t ino, int obj) 105{ 106 if(n_obj < MAX_OBJECTS) 107 { 108 obj_list[n_obj].dev = dev; 109 obj_list[n_obj].ino = ino; 110 obj_list[n_obj].obj = obj; 111 n_obj++; 112 qsort(obj_list,n_obj,sizeof(objItem),obj_compare); 113 114 } 115 else 116 { 117 // oops! not enough space in the object array 118 fprintf(stderr,"Not enough space in object array\n"); 119 exit(2); 120 } 121} 122 123 124static int find_obj_in_list(dev_t dev, ino_t ino) 125{ 126 objItem *i = NULL; 127 objItem test; 128 129 test.dev = dev; 130 test.ino = ino; 131 132 if(n_obj > 0) 133 { 134 i = bsearch(&test,obj_list,n_obj,sizeof(objItem),obj_compare); 135 } 136 137 if(i) 138 { 139 return i->obj; 140 } 141 return -1; 142} 143 144#define SWAP32(x) ((((x) & 0x000000FF) << 24) | \ 145 (((x) & 0x0000FF00) << 8 ) | \ 146 (((x) & 0x00FF0000) >> 8 ) | \ 147 (((x) & 0xFF000000) >> 24)) 148 149#define SWAP16(x) ((((x) & 0x00FF) << 8) | \ 150 (((x) & 0xFF00) >> 8)) 151 152// This one is easier, since the types are more standard. No funky shifts here. 153static void object_header_little_to_big_endian(yaffs_ObjectHeader* oh) 154{ 155 oh->type = SWAP32(oh->type); // GCC makes enums 32 bits. 156 oh->parentObjectId = SWAP32(oh->parentObjectId); // int 157 oh->sum__NoLongerUsed = SWAP16(oh->sum__NoLongerUsed); // __u16 - Not used, but done for completeness. 158 // name = skip. Char array. Not swapped. 159 oh->yst_mode = SWAP32(oh->yst_mode); 160#ifdef CONFIG_YAFFS_WINCE // WinCE doesn't implement this, but we need to just in case. 161 // In fact, WinCE would be *THE* place where this would be an issue! 162 oh->notForWinCE[0] = SWAP32(oh->notForWinCE[0]); 163 oh->notForWinCE[1] = SWAP32(oh->notForWinCE[1]); 164 oh->notForWinCE[2] = SWAP32(oh->notForWinCE[2]); 165 oh->notForWinCE[3] = SWAP32(oh->notForWinCE[3]); 166 oh->notForWinCE[4] = SWAP32(oh->notForWinCE[4]); 167#else 168 // Regular POSIX. 169 oh->yst_uid = SWAP32(oh->yst_uid); 170 oh->yst_gid = SWAP32(oh->yst_gid); 171 oh->yst_atime = SWAP32(oh->yst_atime); 172 oh->yst_mtime = SWAP32(oh->yst_mtime); 173 oh->yst_ctime = SWAP32(oh->yst_ctime); 174#endif 175 176 oh->fileSize = SWAP32(oh->fileSize); // Aiee. An int... signed, at that! 177 oh->equivalentObjectId = SWAP32(oh->equivalentObjectId); 178 // alias - char array. 179 oh->yst_rdev = SWAP32(oh->yst_rdev); 180 181#ifdef CONFIG_YAFFS_WINCE 182 oh->win_ctime[0] = SWAP32(oh->win_ctime[0]); 183 oh->win_ctime[1] = SWAP32(oh->win_ctime[1]); 184 oh->win_atime[0] = SWAP32(oh->win_atime[0]); 185 oh->win_atime[1] = SWAP32(oh->win_atime[1]); 186 oh->win_mtime[0] = SWAP32(oh->win_mtime[0]); 187 oh->win_mtime[1] = SWAP32(oh->win_mtime[1]); 188 oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); 189 oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); 190 oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); 191 oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); 192 oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); 193 oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); 194#else 195 oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); 196 oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); 197 oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); 198 oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); 199 oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); 200 oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); 201 oh->roomToGrow[6] = SWAP32(oh->roomToGrow[6]); 202 oh->roomToGrow[7] = SWAP32(oh->roomToGrow[7]); 203 oh->roomToGrow[8] = SWAP32(oh->roomToGrow[8]); 204 oh->roomToGrow[9] = SWAP32(oh->roomToGrow[9]); 205 oh->roomToGrow[10] = SWAP32(oh->roomToGrow[10]); 206 oh->roomToGrow[11] = SWAP32(oh->roomToGrow[11]); 207#endif 208} 209 210/* This little function converts a little endian tag to a big endian tag. 211 * NOTE: The tag is not usable after this other than calculating the CRC 212 * with. 213 */ 214static void little_to_big_endian(yaffs_PackedTags2 *pt) 215{ 216 pt->t.sequenceNumber = SWAP32(pt->t.sequenceNumber); 217 pt->t.objectId = SWAP32(pt->t.objectId); 218 pt->t.chunkId = SWAP32(pt->t.chunkId); 219 pt->t.byteCount = SWAP32(pt->t.byteCount); 220} 221 222static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes) 223{ 224 char spare[spareSize]; 225 yaffs_ExtendedTags t; 226 yaffs_PackedTags2 *pt = (yaffs_PackedTags2 *)spare; 227 228 memset(spare, 0xff, spareSize); 229 230 error = write(outFile,data,chunkSize); 231 if(error < 0) return error; 232 233 yaffs_InitialiseTags(&t); 234 235 t.chunkId = chunkId; 236// t.serialNumber = 0; 237 t.serialNumber = 1; // **CHECK** 238 t.byteCount = nBytes; 239 t.objectId = objId; 240 241 t.sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; 242 243// added NCB **CHECK** 244 t.chunkUsed = 1; 245 246 nPages++; 247 248 yaffs_PackTags2(pt,&t); 249 250 if (convert_endian) 251 { 252 little_to_big_endian(pt); 253 } 254 255// return write(outFile,&pt,sizeof(yaffs_PackedTags2)); 256 return write(outFile,spare, spareSize); 257 258} 259 260static int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias, const char *secontext) 261{ 262 __u8 bytes[chunkSize]; 263 264 265 yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bytes; 266#ifdef HAVE_SELINUX 267 char *xb = (char *)bytes + sizeof(*oh); 268 int xnamelen = strlen(XATTR_NAME_SELINUX) + 1; 269 int xvalsize = 0; 270 int xreclen = 0; 271 272 if (secontext) { 273 xvalsize = strlen(secontext) + 1; 274 xreclen = sizeof(int) + xnamelen + xvalsize; 275 } 276#endif 277 278 memset(bytes,0xff,sizeof(bytes)); 279 280 oh->type = t; 281 282 oh->parentObjectId = parent; 283 284 strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH); 285 286#ifdef HAVE_SELINUX 287 if (xreclen) { 288 memcpy(xb, &xreclen, sizeof(int)); 289 xb += sizeof(int); 290 strcpy(xb, XATTR_NAME_SELINUX); 291 xb += xnamelen; 292 memcpy(xb, secontext, xvalsize); 293 } 294#endif 295 296 if(t != YAFFS_OBJECT_TYPE_HARDLINK) 297 { 298 oh->yst_mode = s->st_mode; 299 oh->yst_uid = s->st_uid; 300// NCB 12/9/02 oh->yst_gid = s->yst_uid; 301 oh->yst_gid = s->st_gid; 302 oh->yst_atime = s->st_atime; 303 oh->yst_mtime = s->st_mtime; 304 oh->yst_ctime = s->st_ctime; 305 oh->yst_rdev = s->st_rdev; 306 } 307 308 if(t == YAFFS_OBJECT_TYPE_FILE) 309 { 310 oh->fileSize = s->st_size; 311 } 312 313 if(t == YAFFS_OBJECT_TYPE_HARDLINK) 314 { 315 oh->equivalentObjectId = equivalentObj; 316 } 317 318 if(t == YAFFS_OBJECT_TYPE_SYMLINK) 319 { 320 strncpy(oh->alias,alias,YAFFS_MAX_ALIAS_LENGTH); 321 } 322 323 if (convert_endian) 324 { 325 object_header_little_to_big_endian(oh); 326 } 327 328 return write_chunk(bytes,objId,0,0xffff); 329 330} 331 332static void fix_stat(const char *path, struct stat *s) 333{ 334 path += source_path_len; 335 fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode); 336} 337 338static int process_directory(int parent, const char *path, int fixstats) 339{ 340 341 DIR *dir; 342 struct dirent *entry; 343 char *secontext = NULL; 344 345 nDirectories++; 346 347 dir = opendir(path); 348 349 if(dir) 350 { 351 while((entry = readdir(dir)) != NULL) 352 { 353 354 /* Ignore . and .. */ 355 if(strcmp(entry->d_name,".") && 356 strcmp(entry->d_name,"..")) 357 { 358 char full_name[500]; 359#ifdef HAVE_SELINUX 360 char *suffix, dest_name[500]; 361 int ret; 362#endif 363 struct stat stats; 364 int equivalentObj; 365 int newObj; 366 367 sprintf(full_name,"%s/%s",path,entry->d_name); 368 369 lstat(full_name,&stats); 370 371#ifdef HAVE_SELINUX 372 if (sehnd) { 373 suffix = full_name + seprefixlen; 374 ret = snprintf(dest_name, 375 sizeof dest_name, 376 "%s%s", mntpoint, 377 suffix); 378 if (ret < 0 || 379 (size_t) ret >= sizeof dest_name) { 380 fprintf(stderr, 381 "snprintf failed on %s%s\n", 382 mntpoint, suffix); 383 exit(1); 384 } 385 if (selabel_lookup(sehnd, &secontext, 386 dest_name, 387 stats.st_mode) < 0) { 388 perror("selabel_lookup"); 389 exit(1); 390 } 391 } 392#endif 393 394 if(S_ISLNK(stats.st_mode) || 395 S_ISREG(stats.st_mode) || 396 S_ISDIR(stats.st_mode) || 397 S_ISFIFO(stats.st_mode) || 398 S_ISBLK(stats.st_mode) || 399 S_ISCHR(stats.st_mode) || 400 S_ISSOCK(stats.st_mode)) 401 { 402 403 newObj = obj_id++; 404 nObjects++; 405 406 if (fixstats) { 407 fix_stat(full_name, &stats); 408 } 409 410 //printf("Object %d, %s is a ",newObj,full_name); 411 412 /* We're going to create an object for it */ 413 if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0) 414 { 415 /* we need to make a hard link */ 416 //printf("hard link to object %d\n",equivalentObj); 417 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL, secontext); 418 } 419 else 420 { 421 422 add_obj_to_list(stats.st_dev,stats.st_ino,newObj); 423 424 if(S_ISLNK(stats.st_mode)) 425 { 426 427 char symname[500]; 428 429 memset(symname,0, sizeof(symname)); 430 431 readlink(full_name,symname,sizeof(symname) -1); 432 433 //printf("symlink to \"%s\"\n",symname); 434 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname, secontext); 435 436 } 437 else if(S_ISREG(stats.st_mode)) 438 { 439 //printf("file, "); 440 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL, secontext); 441 442 if(error >= 0) 443 { 444 int h; 445 __u8 bytes[chunkSize]; 446 int nBytes; 447 int chunk = 0; 448 449 h = open(full_name,O_RDONLY); 450 if(h >= 0) 451 { 452 memset(bytes,0xff,sizeof(bytes)); 453 while((nBytes = read(h,bytes,sizeof(bytes))) > 0) 454 { 455 chunk++; 456 write_chunk(bytes,newObj,chunk,nBytes); 457 memset(bytes,0xff,sizeof(bytes)); 458 } 459 if(nBytes < 0) 460 error = nBytes; 461 462 //printf("%d data chunks written\n",chunk); 463 } 464 else 465 { 466 perror("Error opening file"); 467 } 468 close(h); 469 470 } 471 472 } 473 else if(S_ISSOCK(stats.st_mode)) 474 { 475 //printf("socket\n"); 476 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 477 } 478 else if(S_ISFIFO(stats.st_mode)) 479 { 480 //printf("fifo\n"); 481 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 482 } 483 else if(S_ISCHR(stats.st_mode)) 484 { 485 //printf("character device\n"); 486 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 487 } 488 else if(S_ISBLK(stats.st_mode)) 489 { 490 //printf("block device\n"); 491 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 492 } 493 else if(S_ISDIR(stats.st_mode)) 494 { 495 //printf("directory\n"); 496 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL, secontext); 497// NCB modified 10/9/2001 process_directory(1,full_name); 498 process_directory(newObj,full_name,fixstats); 499 } 500 } 501 } 502 else 503 { 504 //printf(" we don't handle this type\n"); 505 } 506 } 507 } 508 closedir(dir); 509 } 510 511 return 0; 512 513} 514 515static void usage(void) 516{ 517 fprintf(stderr,"mkyaffs2image: image building tool for YAFFS2 built "__DATE__"\n"); 518 fprintf(stderr,"usage: mkyaffs2image [-f] [-c <size>] [-s <size>] dir image_file [file_contexts mountpoint] [convert]\n"); 519 fprintf(stderr," -f fix file stat (mods, user, group) for device\n"); 520 fprintf(stderr," -c <size> set the chunk (NAND page) size. default: 2048\n"); 521 fprintf(stderr," -s <size> set the spare (NAND OOB) size. default: 64\n"); 522 fprintf(stderr," dir the directory tree to be converted\n"); 523 fprintf(stderr," image_file the output file to hold the image\n"); 524 fprintf(stderr," file_contexts the file contexts configuration used to assign SELinux file context attributes\n"); 525 fprintf(stderr," mountpoint the directory where this image be mounted on the device\n"); 526 fprintf(stderr," 'convert' produce a big-endian image from a little-endian machine\n"); 527} 528 529int main(int argc, char *argv[]) 530{ 531 int fixstats = 0; 532 struct stat stats; 533 int opt; 534 char *image; 535 char *dir; 536 char *secontext = NULL; 537 538 while ((opt = getopt(argc, argv, "fc:s:")) != -1) { 539 switch (opt) { 540 case 'f': 541 fixstats = 1; 542 break; 543 case 'c': 544 chunkSize = (unsigned)strtoul(optarg, NULL, 0); 545 break; 546 case 's': 547 spareSize = (unsigned)strtoul(optarg, NULL, 0); 548 break; 549 default: 550 usage(); 551 exit(1); 552 } 553 } 554 555 if (!chunkSize || !spareSize) { 556 usage(); 557 exit(1); 558 } 559 560 if ((argc - optind < 2) || (argc - optind > 4)) { 561 usage(); 562 exit(1); 563 } 564 565 dir = argv[optind]; 566#ifdef HAVE_SELINUX 567 seprefixlen = strlen(dir); 568#endif 569 image = argv[optind + 1]; 570 571 if (optind + 2 < argc) { 572 if (!strncmp(argv[optind + 2], "convert", strlen("convert"))) 573 convert_endian = 1; 574 else { 575#ifdef HAVE_SELINUX 576 struct selinux_opt seopts[] = { 577 { SELABEL_OPT_PATH, argv[optind + 2] } 578 }; 579 sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); 580 if (!sehnd) { 581 perror(argv[optind + 2]); 582 usage(); 583 exit(1); 584 } 585 if (optind + 3 >= argc) { 586 usage(); 587 exit(1); 588 } 589 mntpoint = argv[optind + 3]; 590 if (optind + 4 < argc) { 591 if (!strncmp(argv[optind + 4], "convert", strlen("convert"))) 592 convert_endian = 1; 593 } 594#else 595 usage(); 596 exit(1); 597#endif 598 } 599 } 600 601 if(stat(dir,&stats) < 0) 602 { 603 fprintf(stderr,"Could not stat %s\n",dir); 604 exit(1); 605 } 606 607 if(!S_ISDIR(stats.st_mode)) 608 { 609 fprintf(stderr," %s is not a directory\n",dir); 610 exit(1); 611 } 612 613 outFile = open(image,O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); 614 615 616 if(outFile < 0) 617 { 618 fprintf(stderr,"Could not open output file %s\n",image); 619 exit(1); 620 } 621 622 if (fixstats) { 623 int len = strlen(dir); 624 625 if((len >= 4) && (!strcmp(dir + len - 4, "data"))) { 626 source_path_len = len - 4; 627 } else if((len >= 6) && (!strcmp(dir + len - 6, "system"))) { 628 source_path_len = len - 6; 629 } else { 630 fprintf(stderr,"Fixstats (-f) option requested but filesystem is not data or android!\n"); 631 exit(1); 632 } 633 fix_stat(dir, &stats); 634 } 635 636 //printf("Processing directory %s into image file %s\n",dir,image); 637#ifdef HAVE_SELINUX 638 if (sehnd) { 639 if (selabel_lookup(sehnd, &secontext, mntpoint, stats.st_mode) < 0) { 640 perror("selabel_lookup"); 641 exit(1); 642 } 643 } 644#endif 645 646 error = write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL, secontext); 647 if(error) 648 error = process_directory(YAFFS_OBJECTID_ROOT,dir,fixstats); 649 650 close(outFile); 651 652 if(error < 0) 653 { 654 perror("operation incomplete"); 655 exit(1); 656 } 657 else 658 { 659 /* 660 printf("Operation complete.\n" 661 "%d objects in %d directories\n" 662 "%d NAND pages\n",nObjects, nDirectories, nPages); 663 */ 664 } 665 666 close(outFile); 667 668 exit(0); 669} 670 671