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 (at) 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 40 static struct selabel_handle *sehnd; 41 static unsigned int seprefixlen; 42 static 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 53 unsigned source_path_len = 0; 54 unsigned yaffs_traceMask=0; 55 56 #define MAX_OBJECTS 50000 57 58 unsigned chunkSize = 2048; 59 unsigned spareSize = 64; 60 61 const char * mkyaffsimage_c_version = "$Id: mkyaffs2image.c,v 1.2 2005/12/13 00:34:58 tpoynor Exp $"; 62 63 64 typedef struct 65 { 66 dev_t dev; 67 ino_t ino; 68 int obj; 69 } objItem; 70 71 72 static objItem obj_list[MAX_OBJECTS]; 73 static int n_obj = 0; 74 static int obj_id = YAFFS_NOBJECT_BUCKETS + 1; 75 76 static int nObjects, nDirectories, nPages; 77 78 static int outFile; 79 80 static int error; 81 82 #ifdef HAVE_BIG_ENDIAN 83 static int convert_endian = 1; 84 #elif defined(HAVE_LITTLE_ENDIAN) 85 static int convert_endian = 0; 86 #endif 87 88 static 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 104 static 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 124 static 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. 153 static 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 */ 214 static 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 222 static 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 260 static 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 332 static 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 338 static 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 386 char *sepath = NULL; 387 if (dest_name[0] == '/') 388 sepath = strdup(dest_name); 389 else if (asprintf(&sepath, "/%s", dest_name) < 0) 390 sepath = NULL; 391 392 if (!sepath) { 393 perror("malloc"); 394 exit(1); 395 } 396 397 if (selabel_lookup(sehnd, &secontext, 398 sepath, 399 stats.st_mode) < 0) { 400 perror("selabel_lookup"); 401 free(sepath); 402 exit(1); 403 } 404 free(sepath); 405 } 406 #endif 407 408 if(S_ISLNK(stats.st_mode) || 409 S_ISREG(stats.st_mode) || 410 S_ISDIR(stats.st_mode) || 411 S_ISFIFO(stats.st_mode) || 412 S_ISBLK(stats.st_mode) || 413 S_ISCHR(stats.st_mode) || 414 S_ISSOCK(stats.st_mode)) 415 { 416 417 newObj = obj_id++; 418 nObjects++; 419 420 if (fixstats) { 421 fix_stat(full_name, &stats); 422 } 423 424 //printf("Object %d, %s is a ",newObj,full_name); 425 426 /* We're going to create an object for it */ 427 if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0) 428 { 429 /* we need to make a hard link */ 430 //printf("hard link to object %d\n",equivalentObj); 431 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL, secontext); 432 } 433 else 434 { 435 436 add_obj_to_list(stats.st_dev,stats.st_ino,newObj); 437 438 if(S_ISLNK(stats.st_mode)) 439 { 440 441 char symname[500]; 442 443 memset(symname,0, sizeof(symname)); 444 445 readlink(full_name,symname,sizeof(symname) -1); 446 447 //printf("symlink to \"%s\"\n",symname); 448 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname, secontext); 449 450 } 451 else if(S_ISREG(stats.st_mode)) 452 { 453 //printf("file, "); 454 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL, secontext); 455 456 if(error >= 0) 457 { 458 int h; 459 __u8 bytes[chunkSize]; 460 int nBytes; 461 int chunk = 0; 462 463 h = open(full_name,O_RDONLY); 464 if(h >= 0) 465 { 466 memset(bytes,0xff,sizeof(bytes)); 467 while((nBytes = read(h,bytes,sizeof(bytes))) > 0) 468 { 469 chunk++; 470 write_chunk(bytes,newObj,chunk,nBytes); 471 memset(bytes,0xff,sizeof(bytes)); 472 } 473 if(nBytes < 0) 474 error = nBytes; 475 476 //printf("%d data chunks written\n",chunk); 477 } 478 else 479 { 480 perror("Error opening file"); 481 } 482 close(h); 483 484 } 485 486 } 487 else if(S_ISSOCK(stats.st_mode)) 488 { 489 //printf("socket\n"); 490 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 491 } 492 else if(S_ISFIFO(stats.st_mode)) 493 { 494 //printf("fifo\n"); 495 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 496 } 497 else if(S_ISCHR(stats.st_mode)) 498 { 499 //printf("character device\n"); 500 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 501 } 502 else if(S_ISBLK(stats.st_mode)) 503 { 504 //printf("block device\n"); 505 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); 506 } 507 else if(S_ISDIR(stats.st_mode)) 508 { 509 //printf("directory\n"); 510 error = write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL, secontext); 511 // NCB modified 10/9/2001 process_directory(1,full_name); 512 process_directory(newObj,full_name,fixstats); 513 } 514 } 515 } 516 else 517 { 518 //printf(" we don't handle this type\n"); 519 } 520 } 521 } 522 closedir(dir); 523 } 524 525 return 0; 526 527 } 528 529 static void usage(void) 530 { 531 fprintf(stderr,"mkyaffs2image: image building tool for YAFFS2 built "__DATE__"\n"); 532 fprintf(stderr,"usage: mkyaffs2image [-f] [-c <size>] [-s <size>] dir image_file [file_contexts mountpoint] [convert]\n"); 533 fprintf(stderr," -f fix file stat (mods, user, group) for device\n"); 534 fprintf(stderr," -c <size> set the chunk (NAND page) size. default: 2048\n"); 535 fprintf(stderr," -s <size> set the spare (NAND OOB) size. default: 64\n"); 536 fprintf(stderr," dir the directory tree to be converted\n"); 537 fprintf(stderr," image_file the output file to hold the image\n"); 538 fprintf(stderr," file_contexts the file contexts configuration used to assign SELinux file context attributes\n"); 539 fprintf(stderr," mountpoint the directory where this image be mounted on the device\n"); 540 fprintf(stderr," 'convert' produce a big-endian image from a little-endian machine\n"); 541 } 542 543 int main(int argc, char *argv[]) 544 { 545 int fixstats = 0; 546 struct stat stats; 547 int opt; 548 char *image; 549 char *dir; 550 char *secontext = NULL; 551 552 while ((opt = getopt(argc, argv, "fc:s:")) != -1) { 553 switch (opt) { 554 case 'f': 555 fixstats = 1; 556 break; 557 case 'c': 558 chunkSize = (unsigned)strtoul(optarg, NULL, 0); 559 break; 560 case 's': 561 spareSize = (unsigned)strtoul(optarg, NULL, 0); 562 break; 563 default: 564 usage(); 565 exit(1); 566 } 567 } 568 569 if (!chunkSize || !spareSize) { 570 usage(); 571 exit(1); 572 } 573 574 if ((argc - optind < 2) || (argc - optind > 4)) { 575 usage(); 576 exit(1); 577 } 578 579 dir = argv[optind]; 580 #ifdef HAVE_SELINUX 581 seprefixlen = strlen(dir); 582 #endif 583 image = argv[optind + 1]; 584 585 if (optind + 2 < argc) { 586 if (!strncmp(argv[optind + 2], "convert", strlen("convert"))) 587 convert_endian = 1; 588 else { 589 #ifdef HAVE_SELINUX 590 struct selinux_opt seopts[] = { 591 { SELABEL_OPT_PATH, argv[optind + 2] } 592 }; 593 sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); 594 if (!sehnd) { 595 perror(argv[optind + 2]); 596 usage(); 597 exit(1); 598 } 599 if (optind + 3 >= argc) { 600 usage(); 601 exit(1); 602 } 603 mntpoint = argv[optind + 3]; 604 if (optind + 4 < argc) { 605 if (!strncmp(argv[optind + 4], "convert", strlen("convert"))) 606 convert_endian = 1; 607 } 608 #else 609 usage(); 610 exit(1); 611 #endif 612 } 613 } 614 615 if(stat(dir,&stats) < 0) 616 { 617 fprintf(stderr,"Could not stat %s\n",dir); 618 exit(1); 619 } 620 621 if(!S_ISDIR(stats.st_mode)) 622 { 623 fprintf(stderr," %s is not a directory\n",dir); 624 exit(1); 625 } 626 627 outFile = open(image,O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); 628 629 630 if(outFile < 0) 631 { 632 fprintf(stderr,"Could not open output file %s\n",image); 633 exit(1); 634 } 635 636 if (fixstats) { 637 int len = strlen(dir); 638 639 if((len >= 4) && (!strcmp(dir + len - 4, "data"))) { 640 source_path_len = len - 4; 641 } else if((len >= 6) && (!strcmp(dir + len - 6, "system"))) { 642 source_path_len = len - 6; 643 } else { 644 fprintf(stderr,"Fixstats (-f) option requested but filesystem is not data or android!\n"); 645 exit(1); 646 } 647 fix_stat(dir, &stats); 648 } 649 650 //printf("Processing directory %s into image file %s\n",dir,image); 651 #ifdef HAVE_SELINUX 652 if (sehnd) { 653 654 char *sepath = NULL; 655 if (mntpoint[0] == '/') 656 sepath = strdup(mntpoint); 657 else if (asprintf(&sepath, "/%s", mntpoint) < 0) 658 sepath = NULL; 659 660 if (!sepath) { 661 perror("malloc"); 662 exit(1); 663 } 664 665 if (selabel_lookup(sehnd, &secontext, sepath, stats.st_mode) < 0) { 666 perror("selabel_lookup"); 667 free(sepath); 668 exit(1); 669 } 670 671 free(sepath); 672 } 673 #endif 674 675 error = write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL, secontext); 676 if(error) 677 error = process_directory(YAFFS_OBJECTID_ROOT,dir,fixstats); 678 679 close(outFile); 680 681 if(error < 0) 682 { 683 perror("operation incomplete"); 684 exit(1); 685 } 686 else 687 { 688 /* 689 printf("Operation complete.\n" 690 "%d objects in %d directories\n" 691 "%d NAND pages\n",nObjects, nDirectories, nPages); 692 */ 693 } 694 695 close(outFile); 696 697 exit(0); 698 } 699 700