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 * 23 */ 24 25#include <stdlib.h> 26#include <stdio.h> 27#include <fcntl.h> 28#include <sys/types.h> 29#include <sys/stat.h> 30#include <dirent.h> 31#include <string.h> 32#include <unistd.h> 33#include "yaffs_ecc.h" 34#include "yaffs_guts.h" 35 36 37#define MAX_OBJECTS 10000 38 39const char * mkyaffsimage_c_version = "$Id: mkyaffsimage.c,v 1.7 2003/07/16 03:00:48 charles Exp $"; 40 41 42typedef struct 43{ 44 dev_t dev; 45 ino_t ino; 46 int obj; 47} objItem; 48 49 50static objItem obj_list[MAX_OBJECTS]; 51static int n_obj = 0; 52static int obj_id = YAFFS_NOBJECT_BUCKETS + 1; 53 54static int nObjects, nDirectories, nPages; 55 56static int outFile; 57 58static int error; 59 60static int convert_endian = 0; 61 62static int obj_compare(const void *a, const void * b) 63{ 64 objItem *oa, *ob; 65 66 oa = (objItem *)a; 67 ob = (objItem *)b; 68 69 if(oa->dev < ob->dev) return -1; 70 if(oa->dev > ob->dev) return 1; 71 if(oa->ino < ob->ino) return -1; 72 if(oa->ino > ob->ino) return 1; 73 74 return 0; 75} 76 77 78static void add_obj_to_list(dev_t dev, ino_t ino, int obj) 79{ 80 if(n_obj < MAX_OBJECTS) 81 { 82 obj_list[n_obj].dev = dev; 83 obj_list[n_obj].ino = ino; 84 obj_list[n_obj].obj = obj; 85 n_obj++; 86 qsort(obj_list,n_obj,sizeof(objItem),obj_compare); 87 88 } 89 else 90 { 91 // oops! not enough space in the object array 92 fprintf(stderr,"Not enough space in object array\n"); 93 exit(2); 94 } 95} 96 97 98static int find_obj_in_list(dev_t dev, ino_t ino) 99{ 100 objItem *i = NULL; 101 objItem test; 102 103 test.dev = dev; 104 test.ino = ino; 105 106 if(n_obj > 0) 107 { 108 i = bsearch(&test,obj_list,n_obj,sizeof(objItem),obj_compare); 109 } 110 111 if(i) 112 { 113 return i->obj; 114 } 115 return -1; 116} 117 118// NCB added 10/9/2002 119static __u16 yaffs_CalcNameSum(const char *name) 120{ 121 __u16 sum = 0; 122 __u16 i = 1; 123 124 __u8 *bname = (__u8 *)name; 125 126 while (*bname) 127 { 128 sum += (*bname) * i; 129 i++; 130 bname++; 131 } 132 return sum; 133} 134 135 136static void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare) 137{ 138 yaffs_ECCCalculate(data , spare->ecc1); 139 yaffs_ECCCalculate(&data[256] , spare->ecc2); 140} 141 142static void yaffs_CalcTagsECC(yaffs_Tags *tags) 143{ 144 // Todo don't do anything yet. Need to calculate ecc 145 unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes; 146 unsigned i,j; 147 unsigned ecc = 0; 148 unsigned bit = 0; 149 150 // Clear ECC fields 151 if (!convert_endian) 152 { 153 tags->ecc = 0; 154 } 155 else 156 { 157 // Because we're in "munged tag" mode, we have to clear it manually 158 b[6] &= 0xC0; 159 b[7] &= 0x03; 160 } 161 162 for(i = 0; i < 8; i++) 163 { 164// NCB modified 20-9-02 for(j = 1; j &0x7f; j<<=1) 165 for(j = 1; j &0xff; j<<=1) 166 { 167 bit++; 168 if(b[i] & j) 169 { 170 ecc ^= bit; 171 } 172 } 173 } 174 175 // Write out ECC 176 if (!convert_endian) 177 { 178 tags->ecc = ecc; 179 } 180 else 181 { 182 // We have to munge the ECC again. 183 b[6] |= ((ecc >> 6) & 0x3F); 184 b[7] |= ((ecc & 0x3F) << 2); 185 } 186} 187static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr) 188{ 189 yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr; 190 191 //yaffs_CalcTagsECC(tagsPtr); 192 193 sparePtr->tagByte0 = tu->asBytes[0]; 194 sparePtr->tagByte1 = tu->asBytes[1]; 195 sparePtr->tagByte2 = tu->asBytes[2]; 196 sparePtr->tagByte3 = tu->asBytes[3]; 197 sparePtr->tagByte4 = tu->asBytes[4]; 198 sparePtr->tagByte5 = tu->asBytes[5]; 199 sparePtr->tagByte6 = tu->asBytes[6]; 200 sparePtr->tagByte7 = tu->asBytes[7]; 201} 202 203/* This little function converts a little endian tag to a big endian tag. 204 * NOTE: The tag is not usable after this other than calculating the CRC 205 * with. 206 */ 207static void little_to_big_endian(yaffs_Tags *tagsPtr) 208{ 209 yaffs_TagsUnion * tags = (yaffs_TagsUnion* )tagsPtr; // Work in bytes. 210 yaffs_TagsUnion temp; 211 212 memset(&temp, 0, sizeof(temp)); 213 // Ick, I hate magic numbers. 214 temp.asBytes[0] = ((tags->asBytes[2] & 0x0F) << 4) | ((tags->asBytes[1] & 0xF0) >> 4); 215 temp.asBytes[1] = ((tags->asBytes[1] & 0x0F) << 4) | ((tags->asBytes[0] & 0xF0) >> 4); 216 temp.asBytes[2] = ((tags->asBytes[0] & 0x0F) << 4) | ((tags->asBytes[2] & 0x30) >> 2) | ((tags->asBytes[3] & 0xC0) >> 6); 217 temp.asBytes[3] = ((tags->asBytes[3] & 0x3F) << 2) | ((tags->asBytes[2] & 0xC0) >> 6); 218 temp.asBytes[4] = ((tags->asBytes[6] & 0x03) << 6) | ((tags->asBytes[5] & 0xFC) >> 2); 219 temp.asBytes[5] = ((tags->asBytes[5] & 0x03) << 6) | ((tags->asBytes[4] & 0xFC) >> 2); 220 temp.asBytes[6] = ((tags->asBytes[4] & 0x03) << 6) | (tags->asBytes[7] & 0x3F); 221 temp.asBytes[7] = (tags->asBytes[6] & 0xFC) | ((tags->asBytes[7] & 0xC0) >> 6); 222 223 // Now copy it back. 224 tags->asBytes[0] = temp.asBytes[0]; 225 tags->asBytes[1] = temp.asBytes[1]; 226 tags->asBytes[2] = temp.asBytes[2]; 227 tags->asBytes[3] = temp.asBytes[3]; 228 tags->asBytes[4] = temp.asBytes[4]; 229 tags->asBytes[5] = temp.asBytes[5]; 230 tags->asBytes[6] = temp.asBytes[6]; 231 tags->asBytes[7] = temp.asBytes[7]; 232} 233 234static int write_chunk(__u8 *data, __u32 objId, __u32 chunkId, __u32 nBytes) 235{ 236 yaffs_Tags t; 237 yaffs_Spare s; 238 239 error = write(outFile,data,512); 240 if(error < 0) return error; 241 242 memset(&t,0xff,sizeof (yaffs_Tags)); 243 memset(&s,0xff,sizeof (yaffs_Spare)); 244 245 t.chunkId = chunkId; 246 t.serialNumber = 0; 247 t.byteCount = nBytes; 248 t.objectId = objId; 249 250 if (convert_endian) 251 { 252 little_to_big_endian(&t); 253 } 254 255 yaffs_CalcTagsECC(&t); 256 yaffs_LoadTagsIntoSpare(&s,&t); 257 yaffs_CalcECC(data,&s); 258 259 nPages++; 260 261 return write(outFile,&s,sizeof(yaffs_Spare)); 262 263} 264 265#define SWAP32(x) ((((x) & 0x000000FF) << 24) | \ 266 (((x) & 0x0000FF00) << 8 ) | \ 267 (((x) & 0x00FF0000) >> 8 ) | \ 268 (((x) & 0xFF000000) >> 24)) 269 270#define SWAP16(x) ((((x) & 0x00FF) << 8) | \ 271 (((x) & 0xFF00) >> 8)) 272 273// This one is easier, since the types are more standard. No funky shifts here. 274static void object_header_little_to_big_endian(yaffs_ObjectHeader* oh) 275{ 276 oh->type = SWAP32(oh->type); // GCC makes enums 32 bits. 277 oh->parentObjectId = SWAP32(oh->parentObjectId); // int 278 oh->sum__NoLongerUsed = SWAP16(oh->sum__NoLongerUsed); // __u16 - Not used, but done for completeness. 279 // name = skip. Char array. Not swapped. 280 oh->yst_mode = SWAP32(oh->yst_mode); 281#ifdef CONFIG_YAFFS_WINCE // WinCE doesn't implement this, but we need to just in case. 282 // In fact, WinCE would be *THE* place where this would be an issue! 283 oh->notForWinCE[0] = SWAP32(oh->notForWinCE[0]); 284 oh->notForWinCE[1] = SWAP32(oh->notForWinCE[1]); 285 oh->notForWinCE[2] = SWAP32(oh->notForWinCE[2]); 286 oh->notForWinCE[3] = SWAP32(oh->notForWinCE[3]); 287 oh->notForWinCE[4] = SWAP32(oh->notForWinCE[4]); 288#else 289 // Regular POSIX. 290 oh->yst_uid = SWAP32(oh->yst_uid); 291 oh->yst_gid = SWAP32(oh->yst_gid); 292 oh->yst_atime = SWAP32(oh->yst_atime); 293 oh->yst_mtime = SWAP32(oh->yst_mtime); 294 oh->yst_ctime = SWAP32(oh->yst_ctime); 295#endif 296 297 oh->fileSize = SWAP32(oh->fileSize); // Aiee. An int... signed, at that! 298 oh->equivalentObjectId = SWAP32(oh->equivalentObjectId); 299 // alias - char array. 300 oh->yst_rdev = SWAP32(oh->yst_rdev); 301 302#ifdef CONFIG_YAFFS_WINCE 303 oh->win_ctime[0] = SWAP32(oh->win_ctime[0]); 304 oh->win_ctime[1] = SWAP32(oh->win_ctime[1]); 305 oh->win_atime[0] = SWAP32(oh->win_atime[0]); 306 oh->win_atime[1] = SWAP32(oh->win_atime[1]); 307 oh->win_mtime[0] = SWAP32(oh->win_mtime[0]); 308 oh->win_mtime[1] = SWAP32(oh->win_mtime[1]); 309 oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); 310 oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); 311 oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); 312 oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); 313 oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); 314 oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); 315#else 316 oh->roomToGrow[0] = SWAP32(oh->roomToGrow[0]); 317 oh->roomToGrow[1] = SWAP32(oh->roomToGrow[1]); 318 oh->roomToGrow[2] = SWAP32(oh->roomToGrow[2]); 319 oh->roomToGrow[3] = SWAP32(oh->roomToGrow[3]); 320 oh->roomToGrow[4] = SWAP32(oh->roomToGrow[4]); 321 oh->roomToGrow[5] = SWAP32(oh->roomToGrow[5]); 322 oh->roomToGrow[6] = SWAP32(oh->roomToGrow[6]); 323 oh->roomToGrow[7] = SWAP32(oh->roomToGrow[7]); 324 oh->roomToGrow[8] = SWAP32(oh->roomToGrow[8]); 325 oh->roomToGrow[9] = SWAP32(oh->roomToGrow[9]); 326 oh->roomToGrow[10] = SWAP32(oh->roomToGrow[10]); 327 oh->roomToGrow[11] = SWAP32(oh->roomToGrow[11]); 328#endif 329} 330 331static int write_object_header(int objId, yaffs_ObjectType t, struct stat *s, int parent, const char *name, int equivalentObj, const char * alias) 332{ 333 __u8 bytes[512]; 334 335 336 yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bytes; 337 338 memset(bytes,0xff,512); 339 340 oh->type = t; 341 342 oh->parentObjectId = parent; 343 344 strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH); 345 346 347 if(t != YAFFS_OBJECT_TYPE_HARDLINK) 348 { 349 oh->yst_mode = s->st_mode; 350 oh->yst_uid = s->st_uid; 351// NCB 12/9/02 oh->yst_gid = s->yst_uid; 352 oh->yst_gid = s->st_gid; 353 oh->yst_atime = s->st_atime; 354 oh->yst_mtime = s->st_mtime; 355 oh->yst_ctime = s->st_ctime; 356 oh->yst_rdev = s->st_rdev; 357 } 358 359 if(t == YAFFS_OBJECT_TYPE_FILE) 360 { 361 oh->fileSize = s->st_size; 362 } 363 364 if(t == YAFFS_OBJECT_TYPE_HARDLINK) 365 { 366 oh->equivalentObjectId = equivalentObj; 367 } 368 369 if(t == YAFFS_OBJECT_TYPE_SYMLINK) 370 { 371 strncpy(oh->alias,alias,YAFFS_MAX_ALIAS_LENGTH); 372 } 373 374 if (convert_endian) 375 { 376 object_header_little_to_big_endian(oh); 377 } 378 379 return write_chunk(bytes,objId,0,0xffff); 380 381} 382 383 384static int process_directory(int parent, const char *path) 385{ 386 387 DIR *dir; 388 struct dirent *entry; 389 390 nDirectories++; 391 392 dir = opendir(path); 393 394 if(dir) 395 { 396 while((entry = readdir(dir)) != NULL) 397 { 398 399 /* Ignore . and .. */ 400 if(strcmp(entry->d_name,".") && 401 strcmp(entry->d_name,"..")) 402 { 403 char full_name[500]; 404 struct stat stats; 405 int equivalentObj; 406 int newObj; 407 408 sprintf(full_name,"%s/%s",path,entry->d_name); 409 410 lstat(full_name,&stats); 411 412 if(S_ISLNK(stats.st_mode) || 413 S_ISREG(stats.st_mode) || 414 S_ISDIR(stats.st_mode) || 415 S_ISFIFO(stats.st_mode) || 416 S_ISBLK(stats.st_mode) || 417 S_ISCHR(stats.st_mode) || 418 S_ISSOCK(stats.st_mode)) 419 { 420 421 newObj = obj_id++; 422 nObjects++; 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); 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); 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); 455 456 if(error >= 0) 457 { 458 int h; 459 __u8 bytes[512]; 460 int nBytes; 461 int chunk = 0; 462 463 h = open(full_name,O_RDONLY); 464 if(h >= 0) 465 { 466 memset(bytes,0xff,512); 467 while((nBytes = read(h,bytes,512)) > 0) 468 { 469 chunk++; 470 write_chunk(bytes,newObj,chunk,nBytes); 471 memset(bytes,0xff,512); 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); 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); 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); 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); 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); 511// NCB modified 10/9/2001 process_directory(1,full_name); 512 process_directory(newObj,full_name); 513 } 514 } 515 } 516 else 517 { 518 printf(" we don't handle this type\n"); 519 } 520 } 521 } 522 } 523 524 return 0; 525 526} 527 528 529int main(int argc, char *argv[]) 530{ 531 struct stat stats; 532 533 printf("mkyaffsimage: image building tool for YAFFS built "__DATE__"\n"); 534 535 if(argc < 3) 536 { 537 printf("usage: mkyaffsimage dir image_file [convert]\n"); 538 printf(" dir the directory tree to be converted\n"); 539 printf(" image_file the output file to hold the image\n"); 540 printf(" 'convert' produce a big-endian image from a little-endian machine\n"); 541 exit(1); 542 } 543 544 if ((argc == 4) && (!strncmp(argv[3], "convert", strlen("convert")))) 545 { 546 convert_endian = 1; 547 } 548 549 if(stat(argv[1],&stats) < 0) 550 { 551 printf("Could not stat %s\n",argv[1]); 552 exit(1); 553 } 554 555 if(!S_ISDIR(stats.st_mode)) 556 { 557 printf(" %s is not a directory\n",argv[1]); 558 exit(1); 559 } 560 561 outFile = open(argv[2],O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); 562 563 564 if(outFile < 0) 565 { 566 printf("Could not open output file %s\n",argv[2]); 567 exit(1); 568 } 569 570 printf("Processing directory %s into image file %s\n",argv[1],argv[2]); 571 error = write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL); 572 if(error) 573 error = process_directory(YAFFS_OBJECTID_ROOT,argv[1]); 574 575 close(outFile); 576 577 if(error < 0) 578 { 579 perror("operation incomplete"); 580 exit(1); 581 } 582 else 583 { 584 printf("Operation complete.\n" 585 "%d objects in %d directories\n" 586 "%d NAND pages\n",nObjects, nDirectories, nPages); 587 } 588 589 close(outFile); 590 591 exit(0); 592} 593 594