summaryrefslogtreecommitdiff
path: root/fs/jffs2/jffs2_1pass.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/jffs2/jffs2_1pass.c')
-rw-r--r--fs/jffs2/jffs2_1pass.c96
1 files changed, 75 insertions, 21 deletions
diff --git a/fs/jffs2/jffs2_1pass.c b/fs/jffs2/jffs2_1pass.c
index 3fd4e52..3897ce6 100644
--- a/fs/jffs2/jffs2_1pass.c
+++ b/fs/jffs2/jffs2_1pass.c
@@ -1,4 +1,3 @@
-/* vi: set sw=4 ts=4: */
/*
-------------------------------------------------------------------------
* Filename: jffs2.c
@@ -265,20 +264,56 @@ insert_node(struct b_list *list, u32 offset)
}
#ifdef CFG_JFFS2_SORT_FRAGMENTS
+/* Sort data entries with the latest version last, so that if there
+ * is overlapping data the latest version will be used.
+ */
static int compare_inodes(struct b_node *new, struct b_node *old)
{
struct jffs2_raw_inode *jNew = (struct jffs2_raw_inode *)new->offset;
struct jffs2_raw_inode *jOld = (struct jffs2_raw_inode *)old->offset;
- return jNew->version < jOld->version;
+ return jNew->version > jOld->version;
}
+/* Sort directory entries so all entries in the same directory
+ * with the same name are grouped together, with the latest version
+ * last. This makes it easy to eliminate all but the latest version
+ * by marking the previous version dead by setting the inode to 0.
+ */
static int compare_dirents(struct b_node *new, struct b_node *old)
{
struct jffs2_raw_dirent *jNew = (struct jffs2_raw_dirent *)new->offset;
struct jffs2_raw_dirent *jOld = (struct jffs2_raw_dirent *)old->offset;
-
- return jNew->version > jOld->version;
+ 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.
+ */
+ if (jNew->nsize != jOld->nsize)
+ return jNew->nsize > jOld->nsize;
+
+ /* length is also the same, so use ascending sort by name
+ */
+ cmp = strncmp(jNew->name, 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
+ */
+ jOld->ino = 0;
+ return 1;
+ }
+
+ return 0;
}
#endif
@@ -327,12 +362,31 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
struct b_node *b;
struct jffs2_raw_inode *jNode;
u32 totalSize = 0;
- u16 latestVersion = 0;
+ u32 latestVersion = 0;
char *lDest;
char *src;
long ret;
int i;
u32 counter = 0;
+#ifdef CFG_JFFS2_SORT_FRAGMENTS
+ /* Find file size before loading any data, so fragments that
+ * start past the end of file can be ignored. A fragment
+ * that is partially in the file is loaded, so extra data may
+ * be loaded up to the next 4K boundary above the file size.
+ * This shouldn't cause trouble when loading kernel images, so
+ * we will live with it.
+ */
+ for (b = pL->frag.listHead; b != NULL; b = b->next) {
+ jNode = (struct jffs2_raw_inode *) (b->offset);
+ if ((inode == jNode->ino)) {
+ /* get actual file length from the newest node */
+ if (jNode->version >= latestVersion) {
+ totalSize = jNode->isize;
+ latestVersion = jNode->version;
+ }
+ }
+ }
+#endif
for (b = pL->frag.listHead; b != NULL; b = b->next) {
jNode = (struct jffs2_raw_inode *) (b->offset);
@@ -349,11 +403,14 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
putLabeledWord("read_inode: usercompr = ", jNode->usercompr);
putLabeledWord("read_inode: flags = ", jNode->flags);
#endif
+
+#ifndef CFG_JFFS2_SORT_FRAGMENTS
/* get actual file length from the newest node */
if (jNode->version >= latestVersion) {
totalSize = jNode->isize;
latestVersion = jNode->version;
}
+#endif
if(dest) {
src = ((char *) jNode) + sizeof(struct jffs2_raw_inode);
@@ -430,15 +487,11 @@ jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino)
if ((pino == jDir->pino) && (len == jDir->nsize) &&
(jDir->ino) && /* 0 for unlink */
(!strncmp(jDir->name, name, len))) { /* a match */
- if (jDir->version < version) continue;
+ if (jDir->version < version)
+ continue;
- if(jDir->version == 0) {
- /* Is this legal? */
- putstr(" ** WARNING ** ");
- putnstr(jDir->name, jDir->nsize);
- putstr(" is version 0 (in find, ignoring)\r\n");
- } else if(jDir->version == version) {
- /* Im pretty sure this isn't ... */
+ if (jDir->version == version && inode != 0) {
+ /* I'm pretty sure this isn't legal */
putstr(" ** ERROR ** ");
putnstr(jDir->name, jDir->nsize);
putLabeledWord(" has dup version =", version);
@@ -643,15 +696,11 @@ jffs2_1pass_resolve_inode(struct b_lists * pL, u32 ino)
for(b = pL->dir.listHead; b; b = b->next) {
jDir = (struct jffs2_raw_dirent *) (b->offset);
if (ino == jDir->ino) {
- if(jDir->version < version) continue;
+ if (jDir->version < version)
+ continue;
- if(jDir->version == 0) {
- /* Is this legal? */
- putstr(" ** WARNING ** ");
- putnstr(jDir->name, jDir->nsize);
- putstr(" is version 0 (in resolve, ignoring)\r\n");
- } else if(jDir->version == version) {
- /* Im pretty sure this isn't ... */
+ if (jDir->version == version && jDirFound) {
+ /* I'm pretty sure this isn't legal */
putstr(" ** ERROR ** ");
putnstr(jDir->name, jDir->nsize);
putLabeledWord(" has dup version (resolve) = ",
@@ -891,6 +940,11 @@ jffs2_1pass_build_lists(struct part_info * part)
printf("OOPS Cleanmarker has bad size "
"%d != %d\n", node->totlen,
sizeof(struct jffs2_unknown_node));
+ } else if (node->nodetype == JFFS2_NODETYPE_PADDING) {
+ if (node->totlen < sizeof(struct jffs2_unknown_node))
+ printf("OOPS Padding has bad size "
+ "%d < %d\n", node->totlen,
+ sizeof(struct jffs2_unknown_node));
} else {
printf("Unknown node type: %x len %d "
"offset 0x%x\n", node->nodetype,