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