X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=fs%2Fjffs2%2Fjffs2_1pass.c;h=68db8c3ba4accb065619b3325f42c7037878a1b9;hb=25ec2282ebbcd09af0f8f714abbc9bab89659e9a;hp=3fb5db383ec9206fba37f5f4039f635b30c1cce0;hpb=707acd01ded3c60a4e277f7c5432d397897b4dfd;p=oweals%2Fu-boot.git diff --git a/fs/jffs2/jffs2_1pass.c b/fs/jffs2/jffs2_1pass.c index 3fb5db383e..68db8c3ba4 100644 --- a/fs/jffs2/jffs2_1pass.c +++ b/fs/jffs2/jffs2_1pass.c @@ -115,13 +115,15 @@ #include #include #include +#include #include #include +#include #include #include #include #include -#include +#include #include "jffs2_private.h" @@ -174,10 +176,15 @@ static u32 nand_cache_off = (u32)-1; static int read_nand_cached(u32 off, u32 size, u_char *buf) { struct mtdids *id = current_part->dev->id; + struct mtd_info *mtd; u32 bytes_read = 0; size_t retlen; int cpy_bytes; + mtd = get_nand_dev_by_index(id->num); + if (!mtd) + return -1; + while (bytes_read < size) { if ((off + bytes_read < nand_cache_off) || (off + bytes_read >= nand_cache_off+NAND_CACHE_SIZE)) { @@ -194,8 +201,8 @@ static int read_nand_cached(u32 off, u32 size, u_char *buf) } retlen = NAND_CACHE_SIZE; - if (nand_read(&nand_info[id->num], nand_cache_off, - &retlen, nand_cache) != 0 || + if (nand_read(mtd, nand_cache_off, + &retlen, nand_cache) < 0 || retlen != NAND_CACHE_SIZE) { printf("read_nand_cached: error reading nand off %#x size %d bytes\n", nand_cache_off, NAND_CACHE_SIZE); @@ -294,7 +301,7 @@ static int read_onenand_cached(u32 off, u32 size, u_char *buf) retlen = ONENAND_CACHE_SIZE; if (onenand_read(&onenand_mtd, onenand_cache_off, retlen, - &retlen, onenand_cache) != 0 || + &retlen, onenand_cache) < 0 || retlen != ONENAND_CACHE_SIZE) { printf("read_onenand_cached: error reading nand off %#x size %d bytes\n", onenand_cache_off, ONENAND_CACHE_SIZE); @@ -545,49 +552,19 @@ static struct b_node * insert_node(struct b_list *list, u32 offset) { struct b_node *new; -#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS - struct b_node *b, *prev; -#endif if (!(new = add_node(list))) { putstr("add_node failed!\r\n"); return NULL; } new->offset = offset; + new->next = NULL; -#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS - if (list->listTail != NULL && list->listCompare(new, list->listTail)) - prev = list->listTail; - else if (list->listLast != NULL && list->listCompare(new, list->listLast)) - prev = list->listLast; + if (list->listTail != NULL) + list->listTail->next = new; else - prev = NULL; - - for (b = (prev ? prev->next : list->listHead); - b != NULL && list->listCompare(new, b); - prev = b, b = b->next) { - list->listLoops++; - } - if (b != NULL) - list->listLast = prev; - - if (b != NULL) { - new->next = b; - if (prev != NULL) - prev->next = new; - else - list->listHead = new; - } else -#endif - { - new->next = (struct b_node *) NULL; - if (list->listTail != NULL) { - list->listTail->next = new; - list->listTail = new; - } else { - list->listTail = list->listHead = new; - } - } + list->listHead = new; + list->listTail = new; return new; } @@ -598,14 +575,18 @@ insert_node(struct b_list *list, u32 offset) */ static int compare_inodes(struct b_node *new, struct b_node *old) { - struct jffs2_raw_inode ojNew; - struct jffs2_raw_inode ojOld; - struct jffs2_raw_inode *jNew = - (struct jffs2_raw_inode *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew); - struct jffs2_raw_inode *jOld = - (struct jffs2_raw_inode *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld); - - return jNew->version > jOld->version; + /* + * Only read in the version info from flash, not the entire inode. + * This can make a big difference to speed if flash is slow. + */ + u32 new_version; + u32 old_version; + get_fl_mem(new->offset + offsetof(struct jffs2_raw_inode, version), + sizeof(new_version), &new_version); + get_fl_mem(old->offset + offsetof(struct jffs2_raw_inode, version), + sizeof(old_version), &old_version); + + return new_version > old_version; } /* Sort directory entries so all entries in the same directory @@ -615,42 +596,45 @@ static int compare_inodes(struct b_node *new, struct b_node *old) */ static int compare_dirents(struct b_node *new, struct b_node *old) { - struct jffs2_raw_dirent ojNew; - struct jffs2_raw_dirent ojOld; - struct jffs2_raw_dirent *jNew = - (struct jffs2_raw_dirent *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew); - struct jffs2_raw_dirent *jOld = - (struct jffs2_raw_dirent *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld); - int cmp; - - /* ascending sort by pino */ - if (jNew->pino != jOld->pino) - return jNew->pino > jOld->pino; - - /* pino is the same, so use ascending sort by nsize, so - * we don't do strncmp unless we really must. + /* + * Using NULL as the buffer for NOR flash prevents the entire node + * being read. This makes most comparisons much quicker as only one + * or two entries from the node will be used most of the time. */ - if (jNew->nsize != jOld->nsize) - return jNew->nsize > jOld->nsize; - - /* length is also the same, so use ascending sort by name - */ - cmp = strncmp((char *)jNew->name, (char *)jOld->name, jNew->nsize); - if (cmp != 0) - return cmp > 0; - - /* we have duplicate names in this directory, so use ascending - * sort by version - */ - if (jNew->version > jOld->version) { - /* since jNew is newer, we know jOld is not valid, so - * mark it with inode 0 and it will not be used + struct jffs2_raw_dirent *jNew = get_node_mem(new->offset, NULL); + struct jffs2_raw_dirent *jOld = get_node_mem(old->offset, NULL); + int cmp; + int ret; + + if (jNew->pino != jOld->pino) { + /* ascending sort by pino */ + ret = jNew->pino > jOld->pino; + } else if (jNew->nsize != jOld->nsize) { + /* + * pino is the same, so use ascending sort by nsize, + * so we don't do strncmp unless we really must. */ - jOld->ino = 0; - return 1; + ret = jNew->nsize > jOld->nsize; + } else { + /* + * length is also the same, so use ascending sort by name + */ + cmp = strncmp((char *)jNew->name, (char *)jOld->name, + jNew->nsize); + if (cmp != 0) { + ret = cmp > 0; + } else { + /* + * we have duplicate names in this directory, + * so use ascending sort by version + */ + ret = jNew->version > jOld->version; + } } + put_fl_mem(jNew, NULL); + put_fl_mem(jOld, NULL); - return 0; + return ret; } #endif @@ -719,12 +703,23 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest) } put_fl_mem(jNode, pL->readbuf); } + /* + * If no destination is provided, we are done. + * Just return the total size. + */ + if (!dest) + return totalSize; #endif for (b = pL->frag.listHead; b != NULL; b = b->next) { - jNode = (struct jffs2_raw_inode *) get_node_mem(b->offset, - pL->readbuf); - if ((inode == jNode->ino)) { + /* + * Copy just the node and not the data at this point, + * since we don't yet know if we need this data. + */ + jNode = (struct jffs2_raw_inode *)get_fl_mem(b->offset, + sizeof(struct jffs2_raw_inode), + pL->readbuf); + if (inode == jNode->ino) { #if 0 putLabeledWord("\r\n\r\nread_inode: totlen = ", jNode->totlen); putLabeledWord("read_inode: inode = ", jNode->ino); @@ -747,7 +742,15 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest) #endif if(dest) { - src = ((uchar *) jNode) + sizeof(struct jffs2_raw_inode); + /* + * Now that the inode has been checked, + * read the entire inode, including data. + */ + put_fl_mem(jNode, pL->readbuf); + jNode = (struct jffs2_raw_inode *) + get_node_mem(b->offset, pL->readbuf); + src = ((uchar *)jNode) + + sizeof(struct jffs2_raw_inode); /* ignore data behind latest known EOF */ if (jNode->offset > totalSize) { put_fl_mem(jNode, pL->readbuf); @@ -832,7 +835,6 @@ jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino) jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset, pL->readbuf); if ((pino == jDir->pino) && (len == jDir->nsize) && - (jDir->ino) && /* 0 for unlink */ (!strncmp((char *)jDir->name, name, len))) { /* a match */ if (jDir->version < version) { put_fl_mem(jDir, pL->readbuf); @@ -953,16 +955,47 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino) for (b = pL->dir.listHead; b; b = b->next) { jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset, pL->readbuf); - if ((pino == jDir->pino) && (jDir->ino)) { /* ino=0 -> unlink */ + if (pino == jDir->pino) { u32 i_version = 0; - struct jffs2_raw_inode ojNode; struct jffs2_raw_inode *jNode, *i = NULL; - struct b_node *b2 = pL->frag.listHead; + struct b_node *b2; - while (b2) { +#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS + /* Check for more recent versions of this file */ + int match; + do { + struct b_node *next = b->next; + struct jffs2_raw_dirent *jDirNext; + if (!next) + break; + jDirNext = (struct jffs2_raw_dirent *) + get_node_mem(next->offset, NULL); + match = jDirNext->pino == jDir->pino && + jDirNext->nsize == jDir->nsize && + strncmp((char *)jDirNext->name, + (char *)jDir->name, + jDir->nsize) == 0; + if (match) { + /* Use next. It is more recent */ + b = next; + /* Update buffer with the new info */ + *jDir = *jDirNext; + } + put_fl_mem(jDirNext, NULL); + } while (match); +#endif + if (jDir->ino == 0) { + /* Deleted file */ + put_fl_mem(jDir, pL->readbuf); + continue; + } + + for (b2 = pL->frag.listHead; b2; b2 = b2->next) { jNode = (struct jffs2_raw_inode *) - get_fl_mem(b2->offset, sizeof(ojNode), &ojNode); - if (jNode->ino == jDir->ino && jNode->version >= i_version) { + get_fl_mem(b2->offset, sizeof(*jNode), + NULL); + if (jNode->ino == jDir->ino && + jNode->version >= i_version) { i_version = jNode->version; if (i) put_fl_mem(i, NULL); @@ -975,7 +1008,7 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino) sizeof(*i), NULL); } - b2 = b2->next; + put_fl_mem(jNode, NULL); } dump_inode(pL, jDir, i); @@ -1302,7 +1335,7 @@ int jffs2_sum_scan_sumnode(struct part_info *part, uint32_t offset, struct b_lists *pL) { struct jffs2_unknown_node crcnode; - int ret, ofs; + int ret, __maybe_unused ofs; uint32_t crc; ofs = part->sector_size - sumsize; @@ -1424,7 +1457,7 @@ dump_dirents(struct b_lists *pL) } #endif -#define DEFAULT_EMPTY_SCAN_SIZE 4096 +#define DEFAULT_EMPTY_SCAN_SIZE 256 static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) { @@ -1438,14 +1471,14 @@ static u32 jffs2_1pass_build_lists(struct part_info * part) { struct b_lists *pL; - struct jffs2_unknown_node *node; + union jffs2_node_union *node; u32 nr_sectors; u32 i; u32 counter4 = 0; u32 counterF = 0; u32 counterN = 0; u32 max_totlen = 0; - u32 buf_size = DEFAULT_EMPTY_SCAN_SIZE; + u32 buf_size; char *buf; nr_sectors = lldiv(part->size, part->sector_size); @@ -1457,7 +1490,7 @@ jffs2_1pass_build_lists(struct part_info * part) /* if we are building a list we need to refresh the cache. */ jffs_init_1pass_list(part); pL = (struct b_lists *)part->jffs2_priv; - buf = malloc(buf_size); + buf = malloc(DEFAULT_EMPTY_SCAN_SIZE); puts ("Scanning JFFS2 FS: "); /* start at the beginning of the partition */ @@ -1472,7 +1505,12 @@ jffs2_1pass_build_lists(struct part_info * part) uint32_t sumlen; int ret; #endif + /* Indicates a sector with a CLEANMARKER was found */ + int clean_sector = 0; + struct jffs2_unknown_node crcnode; + /* Set buf_size to maximum length */ + buf_size = DEFAULT_EMPTY_SCAN_SIZE; WATCHDOG_RESET(); #ifdef CONFIG_JFFS2_SUMMARY @@ -1547,6 +1585,11 @@ jffs2_1pass_build_lists(struct part_info * part) ofs += sector_ofs; prevofs = ofs - 1; + /* + * Set buf_size down to the minimum size required. + * This prevents reading in chunks of flash data unnecessarily. + */ + buf_size = sizeof(union jffs2_node_union); scan_more: while (ofs < sector_ofs + part->sector_size) { @@ -1558,9 +1601,10 @@ jffs2_1pass_build_lists(struct part_info * part) } prevofs = ofs; if (sector_ofs + part->sector_size < - ofs + sizeof(*node)) + ofs + sizeof(struct jffs2_unknown_node)) break; - if (buf_ofs + buf_len < ofs + sizeof(*node)) { + if (buf_ofs + buf_len < + ofs + sizeof(struct jffs2_unknown_node)) { buf_len = min_t(uint32_t, buf_size, sector_ofs + part->sector_size - ofs); get_fl_mem((u32)part->offset + ofs, buf_len, @@ -1568,7 +1612,7 @@ jffs2_1pass_build_lists(struct part_info * part) buf_ofs = ofs; } - node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; + node = (union jffs2_node_union *)&buf[ofs - buf_ofs]; if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { uint32_t inbuf_ofs; @@ -1589,6 +1633,14 @@ jffs2_1pass_build_lists(struct part_info * part) ofs += 4; } /* Ran off end. */ + /* + * If this sector had a clean marker at the + * beginning, and immediately following this + * have been a bunch of FF bytes, treat the + * entire sector as empty. + */ + if (clean_sector) + break; /* See how much more there is to read in this * eraseblock... @@ -1610,30 +1662,58 @@ jffs2_1pass_build_lists(struct part_info * part) buf_ofs = ofs; goto more_empty; } - if (node->magic != JFFS2_MAGIC_BITMASK || - !hdr_crc(node)) { + /* + * Found something not erased in the sector, so reset + * the 'clean_sector' flag. + */ + clean_sector = 0; + if (node->u.magic != JFFS2_MAGIC_BITMASK) { + ofs += 4; + counter4++; + continue; + } + + crcnode.magic = node->u.magic; + crcnode.nodetype = node->u.nodetype | JFFS2_NODE_ACCURATE; + crcnode.totlen = node->u.totlen; + crcnode.hdr_crc = node->u.hdr_crc; + if (!hdr_crc(&crcnode)) { ofs += 4; counter4++; continue; } - if (ofs + node->totlen > - sector_ofs + part->sector_size) { + + if (ofs + node->u.totlen > sector_ofs + part->sector_size) { ofs += 4; counter4++; continue; } + + if (!(node->u.nodetype & JFFS2_NODE_ACCURATE)) { + DEBUGF("Obsolete node type: %x len %d offset 0x%x\n", + node->u.nodetype, node->u.totlen, ofs); + ofs += ((node->u.totlen + 3) & ~3); + counterF++; + continue; + } + /* if its a fragment add it */ - switch (node->nodetype) { + switch (node->u.nodetype) { case JFFS2_NODETYPE_INODE: - if (buf_ofs + buf_len < ofs + sizeof(struct - jffs2_raw_inode)) { + if (buf_ofs + buf_len < + ofs + sizeof(struct jffs2_raw_inode)) { + buf_len = min_t(uint32_t, + sizeof(struct jffs2_raw_inode), + sector_ofs + + part->sector_size - + ofs); get_fl_mem((u32)part->offset + ofs, buf_len, buf); buf_ofs = ofs; node = (void *)buf; } - if (!inode_crc((struct jffs2_raw_inode *) node)) - break; + if (!inode_crc((struct jffs2_raw_inode *)node)) + break; if (insert_node(&pL->frag, (u32) part->offset + ofs) == NULL) { @@ -1641,8 +1721,8 @@ jffs2_1pass_build_lists(struct part_info * part) jffs2_free_cache(part); return 0; } - if (max_totlen < node->totlen) - max_totlen = node->totlen; + if (max_totlen < node->u.totlen) + max_totlen = node->u.totlen; break; case JFFS2_NODETYPE_DIRENT: if (buf_ofs + buf_len < ofs + sizeof(struct @@ -1650,6 +1730,11 @@ jffs2_1pass_build_lists(struct part_info * part) ((struct jffs2_raw_dirent *) node)->nsize) { + buf_len = min_t(uint32_t, + node->u.totlen, + sector_ofs + + part->sector_size - + ofs); get_fl_mem((u32)part->offset + ofs, buf_len, buf); buf_ofs = ofs; @@ -1671,37 +1756,55 @@ jffs2_1pass_build_lists(struct part_info * part) jffs2_free_cache(part); return 0; } - if (max_totlen < node->totlen) - max_totlen = node->totlen; + if (max_totlen < node->u.totlen) + max_totlen = node->u.totlen; counterN++; break; case JFFS2_NODETYPE_CLEANMARKER: - if (node->totlen != sizeof(struct jffs2_unknown_node)) + if (node->u.totlen != sizeof(struct jffs2_unknown_node)) printf("OOPS Cleanmarker has bad size " "%d != %zu\n", - node->totlen, + node->u.totlen, sizeof(struct jffs2_unknown_node)); + if (node->u.totlen == + sizeof(struct jffs2_unknown_node) && + ofs == sector_ofs) { + /* + * Found a CLEANMARKER at the beginning + * of the sector. It's in the correct + * place with correct size and CRC. + */ + clean_sector = 1; + } break; case JFFS2_NODETYPE_PADDING: - if (node->totlen < sizeof(struct jffs2_unknown_node)) + if (node->u.totlen < + sizeof(struct jffs2_unknown_node)) printf("OOPS Padding has bad size " "%d < %zu\n", - node->totlen, + node->u.totlen, sizeof(struct jffs2_unknown_node)); break; case JFFS2_NODETYPE_SUMMARY: break; default: printf("Unknown node type: %x len %d offset 0x%x\n", - node->nodetype, - node->totlen, ofs); + node->u.nodetype, + node->u.totlen, ofs); } - ofs += ((node->totlen + 3) & ~3); + ofs += ((node->u.totlen + 3) & ~3); counterF++; } } free(buf); +#if defined(CONFIG_SYS_JFFS2_SORT_FRAGMENTS) + /* + * Sort the lists. + */ + sort_list(&pL->frag); + sort_list(&pL->dir); +#endif putstr("\b\b done.\r\n"); /* close off the dots */ /* We don't care if malloc failed - then each read operation will