1 /* 2 ------------------------------------------------------------------------- 3 * Filename: jffs2.c 4 * Version: $Id: jffs2_1pass.c,v 1.7 2002/01/25 01:56:47 nyet Exp $ 5 * Copyright: Copyright (C) 2001, Russ Dill 6 * Author: Russ Dill <Russ.Dill@asu.edu> 7 * Description: Module to load kernel from jffs2 8 *-----------------------------------------------------------------------*/ 9 /* 10 * some portions of this code are taken from jffs2, and as such, the 11 * following copyright notice is included. 12 * 13 * JFFS2 -- Journalling Flash File System, Version 2. 14 * 15 * Copyright (C) 2001 Red Hat, Inc. 16 * 17 * Created by David Woodhouse <dwmw2@cambridge.redhat.com> 18 * 19 * The original JFFS, from which the design for JFFS2 was derived, 20 * was designed and implemented by Axis Communications AB. 21 * 22 * The contents of this file are subject to the Red Hat eCos Public 23 * License Version 1.1 (the "Licence"); you may not use this file 24 * except in compliance with the Licence. You may obtain a copy of 25 * the Licence at http://www.redhat.com/ 26 * 27 * Software distributed under the Licence is distributed on an "AS IS" 28 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. 29 * See the Licence for the specific language governing rights and 30 * limitations under the Licence. 31 * 32 * The Original Code is JFFS2 - Journalling Flash File System, version 2 33 * 34 * Alternatively, the contents of this file may be used under the 35 * terms of the GNU General Public License version 2 (the "GPL"), in 36 * which case the provisions of the GPL are applicable instead of the 37 * above. If you wish to allow the use of your version of this file 38 * only under the terms of the GPL and not to allow others to use your 39 * version of this file under the RHEPL, indicate your decision by 40 * deleting the provisions above and replace them with the notice and 41 * other provisions required by the GPL. If you do not delete the 42 * provisions above, a recipient may use your version of this file 43 * under either the RHEPL or the GPL. 44 * 45 * $Id: jffs2_1pass.c,v 1.7 2002/01/25 01:56:47 nyet Exp $ 46 * 47 */ 48 49 /* Ok, so anyone who knows the jffs2 code will probably want to get a papar 50 * bag to throw up into before reading this code. I looked through the jffs2 51 * code, the caching scheme is very elegant. I tried to keep the version 52 * for a bootloader as small and simple as possible. Instead of worring about 53 * unneccesary data copies, node scans, etc, I just optimized for the known 54 * common case, a kernel, which looks like: 55 * (1) most pages are 4096 bytes 56 * (2) version numbers are somewhat sorted in acsending order 57 * (3) multiple compressed blocks making up one page is uncommon 58 * 59 * So I create a linked list of decending version numbers (insertions at the 60 * head), and then for each page, walk down the list, until a matching page 61 * with 4096 bytes is found, and then decompress the watching pages in 62 * reverse order. 63 * 64 */ 65 66 /* 67 * Adapted by Nye Liu <nyet@zumanetworks.com> and 68 * Rex Feany <rfeany@zumanetworks.com> 69 * on Jan/2002 for U-Boot. 70 * 71 * Clipped out all the non-1pass functions, cleaned up warnings, 72 * wrappers, etc. No major changes to the code. 73 * Please, he really means it when he said have a paper bag 74 * handy. We needed it ;). 75 * 76 */ 77 78 #include <common.h> 79 #include <config.h> 80 #include <malloc.h> 81 #include <linux/stat.h> 82 #include <linux/time.h> 83 84 #if (CONFIG_COMMANDS & CFG_CMD_JFFS2) 85 86 #include <jffs2/jffs2.h> 87 #include <jffs2/jffs2_1pass.h> 88 89 #include "jffs2_private.h" 90 91 /* Compression names */ 92 static char *compr_names[] = { 93 "NONE", 94 "ZERO", 95 "RTIME", 96 "RUBINMIPS", 97 "COPY", 98 "DYNRUBIN", 99 "ZLIB" }; 100 101 static char spinner[] = { '|', '\\', '-', '/' }; 102 103 #define DEBUG 104 #ifdef DEBUG 105 # define DEBUGF(fmt,args...) printf(fmt ,##args) 106 #else 107 # define DEBUGF(fmt,args...) 108 #endif 109 110 #define MALLOC_CHUNK (10*1024) 111 112 113 static struct b_node * 114 add_node(struct b_node *tail, u32 * count, u32 * memBase) 115 { 116 u32 index; 117 u32 memLimit; 118 struct b_node *b; 119 120 index = (*count) * sizeof(struct b_node) % MALLOC_CHUNK; 121 memLimit = MALLOC_CHUNK; 122 123 #if 0 124 putLabeledWord("add_node: index = ", index); 125 putLabeledWord("add_node: memLimit = ", memLimit); 126 putLabeledWord("add_node: memBase = ", *memBase); 127 #endif 128 129 /* we need not keep a list of bases since we'll never free the */ 130 /* memory, just jump the the kernel */ 131 if ((index == 0) || (index > memLimit)) { /* we need mode space before we continue */ 132 if ((*memBase = (u32) mmalloc(MALLOC_CHUNK)) == (u32) NULL) { 133 putstr("add_node: malloc failed\n"); 134 return NULL; 135 } 136 #if 0 137 putLabeledWord("add_node: alloced a new membase at ", *memBase); 138 #endif 139 140 } 141 /* now we have room to add it. */ 142 b = (struct b_node *) (*memBase + index); 143 144 /* null on first call */ 145 if (tail) 146 tail->next = b; 147 148 #if 0 149 putLabeledWord("add_node: tail = ", (u32) tail); 150 if (tail) 151 putLabeledWord("add_node: tail->next = ", (u32) tail->next); 152 153 #endif 154 155 #if 0 156 putLabeledWord("add_node: mb+i = ", (u32) (*memBase + index)); 157 putLabeledWord("add_node: b = ", (u32) b); 158 #endif 159 (*count)++; 160 b->next = (struct b_node *) NULL; 161 return b; 162 163 } 164 165 /* we know we have empties at the start offset so we will hop */ 166 /* t points that would be non F if there were a node here to speed this up. */ 167 struct jffs2_empty_node { 168 u32 first; 169 u32 second; 170 }; 171 172 static u32 173 jffs2_scan_empty(u32 start_offset, struct part_info *part) 174 { 175 u32 max = part->size - sizeof(struct jffs2_raw_inode); 176 177 /* this would be either dir node_crc or frag isize */ 178 u32 offset = start_offset + 32; 179 struct jffs2_empty_node *node; 180 181 start_offset += 4; 182 while (offset < max) { 183 node = (struct jffs2_empty_node *) (part->offset + offset); 184 if ((node->first == 0xFFFFFFFF) && (node->second == 0xFFFFFFFF)) { 185 /* we presume that there were no nodes in between and advance in a hop */ 186 /* putLabeledWord("\t\tjffs2_scan_empty: empty at offset=",offset); */ 187 start_offset = offset + 4; 188 offset = start_offset + 32; /* orig 32 + 4 bytes for the second==0xfffff */ 189 } else { 190 return start_offset; 191 } 192 } 193 return start_offset; 194 } 195 196 static u32 197 jffs_init_1pass_list(struct part_info *part) 198 { 199 if ( 0 != ( part->jffs2_priv=malloc(sizeof(struct b_lists)))){ 200 struct b_lists *pL =(struct b_lists *)part->jffs2_priv; 201 202 pL->dirListHead = pL->dirListTail = NULL; 203 pL->fragListHead = pL->fragListTail = NULL; 204 pL->dirListCount = 0; 205 pL->dirListMemBase = 0; 206 pL->fragListCount = 0; 207 pL->fragListMemBase = 0; 208 pL->partOffset = 0x0; 209 } 210 return 0; 211 } 212 213 /* find the inode from the slashless name given a parent */ 214 static long 215 jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest) 216 { 217 struct b_node *b; 218 struct jffs2_raw_inode *jNode; 219 u32 totalSize = 1; 220 u32 oldTotalSize = 0; 221 u32 size = 0; 222 char *lDest = (char *) dest; 223 char *src; 224 long ret; 225 int i; 226 u32 counter = 0; 227 char totalSizeSet = 0; 228 229 #if 0 230 b = pL->fragListHead; 231 while (b) { 232 jNode = (struct jffs2_raw_inode *) (b->offset); 233 if ((inode == jNode->ino)) { 234 putLabeledWord("\r\n\r\nread_inode: totlen = ", jNode->totlen); 235 putLabeledWord("read_inode: inode = ", jNode->ino); 236 putLabeledWord("read_inode: version = ", jNode->version); 237 putLabeledWord("read_inode: isize = ", jNode->isize); 238 putLabeledWord("read_inode: offset = ", jNode->offset); 239 putLabeledWord("read_inode: csize = ", jNode->csize); 240 putLabeledWord("read_inode: dsize = ", jNode->dsize); 241 putLabeledWord("read_inode: compr = ", jNode->compr); 242 putLabeledWord("read_inode: usercompr = ", jNode->usercompr); 243 putLabeledWord("read_inode: flags = ", jNode->flags); 244 } 245 246 b = b->next; 247 } 248 249 #endif 250 251 #if 1 252 b = pL->fragListHead; 253 while (b && (size < totalSize)) { 254 jNode = (struct jffs2_raw_inode *) (b->offset); 255 if ((inode == jNode->ino)) { 256 if ((jNode->isize == oldTotalSize) && (jNode->isize > totalSize)) { 257 /* 2 consecutive isizes indicate file length */ 258 totalSize = jNode->isize; 259 totalSizeSet = 1; 260 } else if (!totalSizeSet) { 261 totalSize = size + jNode->dsize + 1; 262 } 263 oldTotalSize = jNode->isize; 264 265 if(dest) { 266 src = ((char *) jNode) + sizeof(struct jffs2_raw_inode); 267 /* lDest = (char *) (dest + (jNode->offset & ~3)); */ 268 lDest = (char *) (dest + jNode->offset); 269 #if 0 270 putLabeledWord("\r\n\r\nread_inode: src = ", src); 271 putLabeledWord("read_inode: dest = ", lDest); 272 putLabeledWord("read_inode: dsize = ", jNode->dsize); 273 putLabeledWord("read_inode: csize = ", jNode->csize); 274 putLabeledWord("read_inode: version = ", jNode->version); 275 putLabeledWord("read_inode: isize = ", jNode->isize); 276 putLabeledWord("read_inode: offset = ", jNode->offset); 277 putLabeledWord("read_inode: compr = ", jNode->compr); 278 putLabeledWord("read_inode: flags = ", jNode->flags); 279 #endif 280 switch (jNode->compr) { 281 case JFFS2_COMPR_NONE: 282 #if 0 283 { 284 int i; 285 286 if ((dest > 0xc0092ff0) && (dest < 0xc0093000)) 287 for (i = 0; i < first->length; i++) { 288 putLabeledWord("\tCOMPR_NONE: src =", src + i); 289 putLabeledWord("\tCOMPR_NONE: length =", first->length); 290 putLabeledWord("\tCOMPR_NONE: dest =", dest + i); 291 putLabeledWord("\tCOMPR_NONE: data =", (unsigned char) *(src + i)); 292 } 293 } 294 #endif 295 296 ret = (unsigned long) ldr_memcpy(lDest, src, jNode->dsize); 297 break; 298 case JFFS2_COMPR_ZERO: 299 ret = 0; 300 for (i = 0; i < jNode->dsize; i++) 301 *(lDest++) = 0; 302 break; 303 case JFFS2_COMPR_RTIME: 304 ret = 0; 305 rtime_decompress(src, lDest, jNode->csize, jNode->dsize); 306 break; 307 case JFFS2_COMPR_DYNRUBIN: 308 /* this is slow but it works */ 309 ret = 0; 310 dynrubin_decompress(src, lDest, jNode->csize, jNode->dsize); 311 break; 312 case JFFS2_COMPR_ZLIB: 313 ret = zlib_decompress(src, lDest, jNode->csize, jNode->dsize); 314 break; 315 default: 316 /* unknown */ 317 putLabeledWord("UNKOWN COMPRESSION METHOD = ", jNode->compr); 318 return -1; 319 break; 320 } 321 } 322 323 size += jNode->dsize; 324 #if 0 325 putLabeledWord("read_inode: size = ", size); 326 putLabeledWord("read_inode: totalSize = ", totalSize); 327 putLabeledWord("read_inode: compr ret = ", ret); 328 #endif 329 } 330 b = b->next; 331 counter++; 332 } 333 #endif 334 335 #if 0 336 putLabeledWord("read_inode: returning = ", size); 337 #endif 338 return size; 339 } 340 341 /* find the inode from the slashless name given a parent */ 342 static u32 343 jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino) 344 { 345 struct b_node *b; 346 struct jffs2_raw_dirent *jDir; 347 int len; 348 u32 counter; 349 u32 version = 0; 350 u32 inode = 0; 351 352 /* name is assumed slash free */ 353 len = strlen(name); 354 355 counter = 0; 356 /* we need to search all and return the inode with the highest version */ 357 for(b = pL->dirListHead;b;b=b->next,counter++) { 358 jDir = (struct jffs2_raw_dirent *) (b->offset); 359 if ((pino == jDir->pino) && (len == jDir->nsize) && (jDir->ino) && /* 0 for unlink */ 360 (!strncmp(jDir->name, name, len))) { /* a match */ 361 if (jDir->version < version) continue; 362 363 if(jDir->version==0) { 364 /* Is this legal? */ 365 putstr(" ** WARNING ** "); 366 putnstr(jDir->name, jDir->nsize); 367 putstr(" is version 0 (in find, ignoring)\r\n"); 368 } else if(jDir->version==version) { 369 /* Im pretty sure this isn't ... */ 370 putstr(" ** ERROR ** "); 371 putnstr(jDir->name, jDir->nsize); 372 putLabeledWord(" has dup version =", version); 373 } 374 inode = jDir->ino; 375 version = jDir->version; 376 } 377 #if 0 378 putstr("\r\nfind_inode:p&l ->"); 379 putnstr(jDir->name, jDir->nsize); 380 putstr("\r\n"); 381 putLabeledWord("pino = ", jDir->pino); 382 putLabeledWord("nsize = ", jDir->nsize); 383 putLabeledWord("b = ", (u32) b); 384 putLabeledWord("counter = ", counter); 385 #endif 386 } 387 return inode; 388 } 389 390 static char *mkmodestr(unsigned long mode, char *str) 391 { 392 static const char *l="xwr"; 393 int mask=1, i; 394 char c; 395 396 switch (mode & S_IFMT) { 397 case S_IFDIR: str[0]='d'; break; 398 case S_IFBLK: str[0]='b'; break; 399 case S_IFCHR: str[0]='c'; break; 400 case S_IFIFO: str[0]='f'; break; 401 case S_IFLNK: str[0]='l'; break; 402 case S_IFSOCK: str[0]='s'; break; 403 case S_IFREG: str[0]='-'; break; 404 default: str[0]='?'; 405 } 406 407 for(i=0;i<9;i++) { 408 c=l[i%3]; 409 str[9-i]=(mode & mask)?c:'-'; 410 mask=mask<<1; 411 } 412 413 if(mode & S_ISUID) str[3]=(mode & S_IXUSR)?'s':'S'; 414 if(mode & S_ISGID) str[6]=(mode & S_IXGRP)?'s':'S'; 415 if(mode & S_ISVTX) str[9]=(mode & S_IXOTH)?'t':'T'; 416 str[10]='\0'; 417 return str; 418 } 419 420 static inline void dump_stat(struct stat *st, const char *name) 421 { 422 char str[20]; 423 char s[64], *p; 424 425 if (st->st_mtime == (time_t)(-1)) /* some ctimes really hate -1 */ 426 st->st_mtime = 1; 427 428 ctime_r(&st->st_mtime, s/*, 64*/); /* newlib ctime doesn't have buflen */ 429 430 if((p=strchr(s,'\n'))!=NULL) *p='\0'; 431 if((p=strchr(s,'\r'))!=NULL) *p='\0'; 432 433 /* 434 printf("%6lo %s %8ld %s %s\n", st->st_mode, mkmodestr(st->st_mode, str), 435 st->st_size, s, name); 436 */ 437 438 printf(" %s %8ld %s %s", mkmodestr(st->st_mode,str), st->st_size, s, name); 439 } 440 441 static inline u32 dump_inode(struct b_lists * pL, struct jffs2_raw_dirent *d, struct jffs2_raw_inode *i) 442 { 443 char fname[256]; 444 struct stat st; 445 446 if(!d || !i) return -1; 447 448 strncpy(fname, d->name, d->nsize); 449 fname[d->nsize]='\0'; 450 451 memset(&st,0,sizeof(st)); 452 453 st.st_mtime=i->mtime; 454 st.st_mode=i->mode; 455 st.st_ino=i->ino; 456 457 /* neither dsize nor isize help us.. do it the long way */ 458 st.st_size=jffs2_1pass_read_inode(pL, i->ino, NULL); 459 460 dump_stat(&st, fname); 461 462 if (d->type == DT_LNK) { 463 unsigned char *src = (unsigned char *) (&i[1]); 464 putstr(" -> "); 465 putnstr(src, (int)i->dsize); 466 } 467 468 putstr("\r\n"); 469 470 return 0; 471 } 472 473 /* list inodes with the given pino */ 474 static u32 475 jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino) 476 { 477 struct b_node *b; 478 struct jffs2_raw_dirent *jDir; 479 480 for(b = pL->dirListHead;b;b=b->next) { 481 jDir = (struct jffs2_raw_dirent *) (b->offset); 482 if ((pino == jDir->pino) && (jDir->ino)) { /* 0 inode for unlink */ 483 u32 i_version=0; 484 struct jffs2_raw_inode *jNode, *i=NULL; 485 struct b_node *b2 = pL->fragListHead; 486 487 while (b2) { 488 jNode = (struct jffs2_raw_inode *) (b2->offset); 489 if (jNode->ino == jDir->ino 490 && jNode->version>=i_version) 491 i=jNode; 492 b2 = b2->next; 493 } 494 495 dump_inode(pL, jDir, i); 496 } 497 } 498 return pino; 499 } 500 501 static u32 502 jffs2_1pass_search_inode(struct b_lists * pL, const char *fname, u32 pino) 503 { 504 int i; 505 char tmp[256]; 506 char working_tmp[256]; 507 char *c; 508 509 /* discard any leading slash */ 510 i = 0; 511 while (fname[i] == '/') 512 i++; 513 strcpy(tmp, &fname[i]); 514 515 while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */ 516 { 517 strncpy(working_tmp, tmp, c - tmp); 518 working_tmp[c - tmp] = '\0'; 519 #if 0 520 putstr("search_inode: tmp = "); 521 putstr(tmp); 522 putstr("\r\n"); 523 putstr("search_inode: wtmp = "); 524 putstr(working_tmp); 525 putstr("\r\n"); 526 putstr("search_inode: c = "); 527 putstr(c); 528 putstr("\r\n"); 529 #endif 530 for (i = 0; i < strlen(c) - 1; i++) 531 tmp[i] = c[i + 1]; 532 tmp[i] = '\0'; 533 #if 0 534 putstr("search_inode: post tmp = "); 535 putstr(tmp); 536 putstr("\r\n"); 537 #endif 538 539 if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino))) { 540 putstr("find_inode failed for name="); 541 putstr(working_tmp); 542 putstr("\r\n"); 543 return 0; 544 } 545 } 546 /* this is for the bare filename, directories have already been mapped */ 547 if (!(pino = jffs2_1pass_find_inode(pL, tmp, pino))) { 548 putstr("find_inode failed for name="); 549 putstr(tmp); 550 putstr("\r\n"); 551 return 0; 552 } 553 return pino; 554 555 } 556 557 static u32 558 jffs2_1pass_resolve_inode(struct b_lists * pL, u32 ino) 559 { 560 struct b_node *b; 561 struct b_node *b2; 562 struct jffs2_raw_dirent *jDir; 563 struct jffs2_raw_inode *jNode; 564 struct jffs2_raw_dirent *jDirFound = NULL; 565 char tmp[256]; 566 u32 version = 0; 567 u32 pino; 568 unsigned char *src; 569 570 /* we need to search all and return the inode with the highest version */ 571 for(b = pL->dirListHead; b; b=b->next) { 572 jDir = (struct jffs2_raw_dirent *) (b->offset); 573 if (ino == jDir->ino) { 574 if(jDir->version < version) continue; 575 576 if(jDir->version == 0) { 577 /* Is this legal? */ 578 putstr(" ** WARNING ** "); 579 putnstr(jDir->name, jDir->nsize); 580 putstr(" is version 0 (in resolve, ignoring)\r\n"); 581 } else if(jDir->version == version) { 582 /* Im pretty sure this isn't ... */ 583 putstr(" ** ERROR ** "); 584 putnstr(jDir->name, jDir->nsize); 585 putLabeledWord(" has dup version (resolve) = ", 586 version); 587 } 588 589 jDirFound = jDir; 590 version = jDir->version; 591 } 592 } 593 /* now we found the right entry again. (shoulda returned inode*) */ 594 if (jDirFound->type != DT_LNK) 595 return jDirFound->ino; 596 /* so its a soft link so we follow it again. */ 597 b2 = pL->fragListHead; 598 while (b2) { 599 jNode = (struct jffs2_raw_inode *) (b2->offset); 600 if (jNode->ino == jDirFound->ino) { 601 src = (unsigned char *) (b2->offset + sizeof(struct jffs2_raw_inode)); 602 603 #if 0 604 putLabeledWord("\t\t dsize = ", jNode->dsize); 605 putstr("\t\t target = "); 606 putnstr(src, jNode->dsize); 607 putstr("\r\n"); 608 #endif 609 strncpy(tmp, src, jNode->dsize); 610 tmp[jNode->dsize] = '\0'; 611 break; 612 } 613 b2 = b2->next; 614 } 615 /* ok so the name of the new file to find is in tmp */ 616 /* if it starts with a slash it is root based else shared dirs */ 617 if (tmp[0] == '/') 618 pino = 1; 619 else 620 pino = jDirFound->pino; 621 622 return jffs2_1pass_search_inode(pL, tmp, pino); 623 } 624 625 static u32 626 jffs2_1pass_search_list_inodes(struct b_lists * pL, const char *fname, u32 pino) 627 { 628 int i; 629 char tmp[256]; 630 char working_tmp[256]; 631 char *c; 632 633 /* discard any leading slash */ 634 i = 0; 635 while (fname[i] == '/') 636 i++; 637 strcpy(tmp, &fname[i]); 638 working_tmp[0] = '\0'; 639 while ((c = (char *) strchr(tmp, '/'))) /* we are still dired searching */ 640 { 641 strncpy(working_tmp, tmp, c - tmp); 642 working_tmp[c - tmp] = '\0'; 643 for (i = 0; i < strlen(c) - 1; i++) 644 tmp[i] = c[i + 1]; 645 tmp[i] = '\0'; 646 /* only a failure if we arent looking at top level */ 647 if (!(pino = jffs2_1pass_find_inode(pL, working_tmp, pino)) && (working_tmp[0])) { 648 putstr("find_inode failed for name="); 649 putstr(working_tmp); 650 putstr("\r\n"); 651 return 0; 652 } 653 } 654 655 if (tmp[0] && !(pino = jffs2_1pass_find_inode(pL, tmp, pino))) { 656 putstr("find_inode failed for name="); 657 putstr(tmp); 658 putstr("\r\n"); 659 return 0; 660 } 661 /* this is for the bare filename, directories have already been mapped */ 662 if (!(pino = jffs2_1pass_list_inodes(pL, pino))) { 663 putstr("find_inode failed for name="); 664 putstr(tmp); 665 putstr("\r\n"); 666 return 0; 667 } 668 return pino; 669 670 } 671 672 unsigned char 673 jffs2_1pass_rescan_needed(struct part_info *part) 674 { 675 struct b_node *b; 676 struct jffs2_unknown_node *node; 677 struct b_lists *pL=(struct b_lists *)part->jffs2_priv; 678 679 if (part->jffs2_priv == 0){ 680 DEBUGF ("rescan: First time in use\n"); 681 return 1; 682 } 683 /* if we have no list, we need to rescan */ 684 if (pL->fragListCount == 0) { 685 DEBUGF ("rescan: fraglist zero\n"); 686 return 1; 687 } 688 689 /* or if we are scanninga new partition */ 690 if (pL->partOffset != part->offset) { 691 DEBUGF ("rescan: different partition\n"); 692 return 1; 693 } 694 /* but suppose someone reflashed the root partition at the same offset... */ 695 b = pL->dirListHead; 696 while (b) { 697 node = (struct jffs2_unknown_node *) (b->offset); 698 if (node->nodetype != JFFS2_NODETYPE_DIRENT) { 699 DEBUGF ("rescan: fs changed beneath me? (%lx)\n", (unsigned long) b->offset); 700 return 1; 701 } 702 b = b->next; 703 } 704 return 0; 705 } 706 707 static u32 708 jffs2_1pass_build_lists(struct part_info * part) 709 { 710 struct b_lists *pL; 711 struct jffs2_unknown_node *node; 712 u32 offset; 713 u32 max = part->size - sizeof(struct jffs2_raw_inode); 714 u32 counter = 0; 715 u32 counter4 = 0; 716 u32 counterF = 0; 717 u32 counterN = 0; 718 719 /* turn off the lcd. Refreshing the lcd adds 50% overhead to the */ 720 /* jffs2 list building enterprise nope. in newer versions the overhead is */ 721 /* only about 5 %. not enough to inconvenience people for. */ 722 /* lcd_off(); */ 723 724 /* if we are building a list we need to refresh the cache. */ 725 /* note that since we don't free our memory, eventually this will be bad. */ 726 /* but we're a bootldr so what the hell. */ 727 jffs_init_1pass_list(part); 728 pL=(struct b_lists *)part->jffs2_priv; 729 pL->partOffset = part->offset; 730 offset = 0; 731 printf("Scanning JFFS2 FS: "); 732 733 /* start at the beginning of the partition */ 734 while (offset < max) { 735 if (! (++counter%10000)) 736 printf("\b\b%c ", spinner[(counter / 10000) % 4]); 737 738 node = (struct jffs2_unknown_node *) (part->offset + offset); 739 if (node->magic == JFFS2_MAGIC_BITMASK && hdr_crc(node)) { 740 /* if its a fragment add it */ 741 if (node->nodetype == JFFS2_NODETYPE_INODE && inode_crc((struct jffs2_raw_inode *) node)) { 742 if (!(pL->fragListTail = add_node(pL->fragListTail, &(pL->fragListCount), 743 &(pL->fragListMemBase)))) { 744 putstr("add_node failed!\r\n"); 745 return 0; 746 } 747 pL->fragListTail->offset = (u32) (part->offset + offset); 748 if (!pL->fragListHead) 749 pL->fragListHead = pL->fragListTail; 750 } else if (node->nodetype == JFFS2_NODETYPE_DIRENT && 751 dirent_crc((struct jffs2_raw_dirent *) node) && 752 dirent_name_crc((struct jffs2_raw_dirent *) node)) { 753 if (! (counterN%100)) 754 printf("\b\b. "); 755 #if 0 756 printf("Found DIRENT @ 0x%lx\n", offset); 757 putstr("\r\nbuild_lists:p&l ->"); 758 putnstr(((struct jffs2_raw_dirent *) node)->name, ((struct jffs2_raw_dirent *) node)->nsize); 759 putstr("\r\n"); 760 putLabeledWord("\tpino = ", ((struct jffs2_raw_dirent *) node)->pino); 761 putLabeledWord("\tnsize = ", ((struct jffs2_raw_dirent *) node)->nsize); 762 #endif 763 764 if (!(pL->dirListTail = add_node(pL->dirListTail, &(pL->dirListCount), &(pL->dirListMemBase)))) { 765 putstr("add_node failed!\r\n"); 766 return 0; 767 } 768 pL->dirListTail->offset = (u32) (part->offset + offset); 769 #if 0 770 putLabeledWord("\ttail = ", (u32) pL->dirListTail); 771 putstr("\ttailName ->"); 772 putnstr(((struct jffs2_raw_dirent *) (pL->dirListTail->offset))->name, 773 ((struct jffs2_raw_dirent *) (pL->dirListTail->offset))->nsize); 774 putstr("\r\n"); 775 #endif 776 if (!pL->dirListHead) 777 pL->dirListHead = pL->dirListTail; 778 counterN++; 779 } else if (node->nodetype == JFFS2_NODETYPE_CLEANMARKER) { 780 if (node->totlen != sizeof(struct jffs2_unknown_node)) 781 printf("OOPS Cleanmarker has bad size %d != %d\n", node->totlen, sizeof(struct jffs2_unknown_node)); 782 } else { 783 printf("Unknown node type: %x len %d offset 0x%x\n", node->nodetype, node->totlen, offset); 784 } 785 offset += ((node->totlen + 3) & ~3); 786 counterF++; 787 } else if (node->magic == JFFS2_EMPTY_BITMASK && node->nodetype == JFFS2_EMPTY_BITMASK) { 788 offset = jffs2_scan_empty(offset, part); 789 } else { /* if we know nothing of the filesystem, we just step and look. */ 790 offset += 4; 791 counter4++; 792 } 793 /* printf("unknown node magic %4.4x %4.4x @ %lx\n", node->magic, node->nodetype, (unsigned long)node); */ 794 795 } 796 797 putstr("\b\b done.\r\n"); /* close off the dots */ 798 /* turn the lcd back on. */ 799 /* splash(); */ 800 801 #if 0 802 putLabeledWord("dir entries = ", pL->dirListCount); 803 putLabeledWord("frag entries = ", pL->fragListCount); 804 putLabeledWord("+4 increments = ", counter4); 805 putLabeledWord("+file_offset increments = ", counterF); 806 807 #endif 808 809 #undef SHOW_ALL 810 #undef SHOW_ALL_FRAGMENTS 811 812 #ifdef SHOW_ALL 813 { 814 struct b_node *b; 815 struct b_node *b2; 816 struct jffs2_raw_dirent *jDir; 817 struct jffs2_raw_inode *jNode; 818 819 putstr("\r\n\r\n******The directory Entries******\r\n"); 820 b = pL->dirListHead; 821 while (b) { 822 jDir = (struct jffs2_raw_dirent *) (b->offset); 823 putstr("\r\n"); 824 putnstr(jDir->name, jDir->nsize); 825 putLabeledWord("\r\n\tbuild_list: magic = ", jDir->magic); 826 putLabeledWord("\tbuild_list: nodetype = ", jDir->nodetype); 827 putLabeledWord("\tbuild_list: hdr_crc = ", jDir->hdr_crc); 828 putLabeledWord("\tbuild_list: pino = ", jDir->pino); 829 putLabeledWord("\tbuild_list: version = ", jDir->version); 830 putLabeledWord("\tbuild_list: ino = ", jDir->ino); 831 putLabeledWord("\tbuild_list: mctime = ", jDir->mctime); 832 putLabeledWord("\tbuild_list: nsize = ", jDir->nsize); 833 putLabeledWord("\tbuild_list: type = ", jDir->type); 834 putLabeledWord("\tbuild_list: node_crc = ", jDir->node_crc); 835 putLabeledWord("\tbuild_list: name_crc = ", jDir->name_crc); 836 b = b->next; 837 } 838 839 #ifdef SHOW_ALL_FRAGMENTS 840 putstr("\r\n\r\n******The fragment Entries******\r\n"); 841 b = pL->fragListHead; 842 while (b) { 843 jNode = (struct jffs2_raw_inode *) (b->offset); 844 putLabeledWord("\r\n\tbuild_list: FLASH_OFFSET = ", b->offset); 845 putLabeledWord("\tbuild_list: totlen = ", jNode->totlen); 846 putLabeledWord("\tbuild_list: inode = ", jNode->ino); 847 putLabeledWord("\tbuild_list: version = ", jNode->version); 848 putLabeledWord("\tbuild_list: isize = ", jNode->isize); 849 putLabeledWord("\tbuild_list: atime = ", jNode->atime); 850 putLabeledWord("\tbuild_list: offset = ", jNode->offset); 851 putLabeledWord("\tbuild_list: csize = ", jNode->csize); 852 putLabeledWord("\tbuild_list: dsize = ", jNode->dsize); 853 putLabeledWord("\tbuild_list: compr = ", jNode->compr); 854 putLabeledWord("\tbuild_list: usercompr = ", jNode->usercompr); 855 putLabeledWord("\tbuild_list: flags = ", jNode->flags); 856 b = b->next; 857 } 858 #endif /* SHOW_ALL_FRAGMENTS */ 859 } 860 861 #endif /* SHOW_ALL */ 862 /* give visual feedback that we are done scanning the flash */ 863 led_blink(0x0, 0x0, 0x1, 0x1); /* off, forever, on 100ms, off 100ms */ 864 return 1; 865 } 866 867 868 869 870 871 static u32 872 jffs2_1pass_fill_info(struct b_lists * pL, struct b_jffs2_info * piL) 873 { 874 struct b_node *b; 875 struct jffs2_raw_inode *jNode; 876 int i; 877 878 b = pL->fragListHead; 879 for (i = 0; i < JFFS2_NUM_COMPR; i++) { 880 piL->compr_info[i].num_frags = 0; 881 piL->compr_info[i].compr_sum = 0; 882 piL->compr_info[i].decompr_sum = 0; 883 } 884 885 while (b) { 886 jNode = (struct jffs2_raw_inode *) (b->offset); 887 if (jNode->compr < JFFS2_NUM_COMPR) { 888 piL->compr_info[jNode->compr].num_frags++; 889 piL->compr_info[jNode->compr].compr_sum += jNode->csize; 890 piL->compr_info[jNode->compr].decompr_sum += jNode->dsize; 891 } 892 b = b->next; 893 } 894 return 0; 895 } 896 897 898 899 static struct b_lists * 900 jffs2_get_list(struct part_info * part, const char *who) 901 { 902 if (jffs2_1pass_rescan_needed(part)) { 903 if (!jffs2_1pass_build_lists(part)) { 904 printf("%s: Failed to scan JFFSv2 file structure\n", who); 905 return NULL; 906 } 907 } 908 return (struct b_lists *)part->jffs2_priv; 909 } 910 911 912 /* Print directory / file contents */ 913 u32 914 jffs2_1pass_ls(struct part_info * part, const char *fname) 915 { 916 struct b_lists *pl; 917 long ret = 0; 918 u32 inode; 919 920 if (! (pl = jffs2_get_list(part, "ls"))) 921 return 0; 922 923 if (! (inode = jffs2_1pass_search_list_inodes(pl, fname, 1))) { 924 putstr("ls: Failed to scan jffs2 file structure\r\n"); 925 return 0; 926 } 927 928 929 #if 0 930 putLabeledWord("found file at inode = ", inode); 931 putLabeledWord("read_inode returns = ", ret); 932 #endif 933 934 return ret; 935 } 936 937 938 939 940 941 /* Load a file from flash into memory. fname can be a full path */ 942 u32 943 jffs2_1pass_load(char *dest, struct part_info * part, const char *fname) 944 { 945 946 struct b_lists *pl; 947 long ret = 0; 948 u32 inode; 949 950 if (! (pl = jffs2_get_list(part, "load"))) 951 return 0; 952 953 if (! (inode = jffs2_1pass_search_inode(pl, fname, 1))) { 954 putstr("load: Failed to find inode\r\n"); 955 return 0; 956 } 957 958 /* Resolve symlinks */ 959 if (! (inode = jffs2_1pass_resolve_inode(pl, inode))) { 960 putstr("load: Failed to resolve inode structure\r\n"); 961 return 0; 962 } 963 964 if ((ret = jffs2_1pass_read_inode(pl, inode, dest)) < 0) { 965 putstr("load: Failed to read inode\r\n"); 966 return 0; 967 } 968 969 DEBUGF ("load: loaded '%s' to 0x%lx (%ld bytes)\n", fname, 970 (unsigned long) dest, ret); 971 return ret; 972 } 973 974 /* Return information about the fs on this partition */ 975 u32 976 jffs2_1pass_info(struct part_info * part) 977 { 978 struct b_jffs2_info info; 979 struct b_lists *pl; 980 int i; 981 982 if (! (pl = jffs2_get_list(part, "info"))) 983 return 0; 984 985 jffs2_1pass_fill_info(pl, &info); 986 for (i=0; i < JFFS2_NUM_COMPR; i++) { 987 printf("Compression: %s\n", compr_names[i]); 988 printf("\tfrag count: %d\n", info.compr_info[i].num_frags); 989 printf("\tcompressed sum: %d\n", info.compr_info[i].compr_sum); 990 printf("\tuncompressed sum: %d\n", info.compr_info[i].decompr_sum); 991 } 992 return 1; 993 } 994 995 #endif /* CFG_CMD_JFFS2 */ 996