[JFFS2] Fix obsoletion of metadata nodes in jffs2_add_tn_to_tree()
[pandora-kernel.git] / fs / jffs2 / readinode.c
index f461604..12e83f6 100644 (file)
@@ -221,7 +221,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
        uint32_t fn_end = tn->fn->ofs + tn->fn->size;
        struct jffs2_tmp_dnode_info *this;
 
-       dbg_readinode("insert fragment %#04x-%#04x, ver %u\n", tn->fn->ofs, fn_end, tn->version);
+       dbg_readinode("insert fragment %#04x-%#04x, ver %u at %08x\n", tn->fn->ofs, fn_end, tn->version, ref_offset(tn->fn->raw));
 
        /* If a node has zero dsize, we only have to keep if it if it might be the
           node with highest version -- i.e. the one which will end up as f->metadata.
@@ -229,9 +229,16 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
           check anyway. */
        if (!tn->fn->size) {
                if (rii->mdata_tn) {
-                       /* We had a candidate mdata node already */
-                       dbg_readinode("kill old mdata with ver %d\n", rii->mdata_tn->version);
-                       jffs2_kill_tn(c, rii->mdata_tn);
+                       if (rii->mdata_tn->version < tn->version) {
+                               /* We had a candidate mdata node already */
+                               dbg_readinode("kill old mdata with ver %d\n", rii->mdata_tn->version);
+                               jffs2_kill_tn(c, rii->mdata_tn);
+                       } else {
+                               dbg_readinode("kill new mdata with ver %d (older than existing %d\n",
+                                             tn->version, rii->mdata_tn->version);
+                               jffs2_kill_tn(c, tn);
+                               return 0;
+                       }
                }
                rii->mdata_tn = tn;
                dbg_readinode("keep new mdata with ver %d\n", tn->version);
@@ -240,20 +247,16 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 
        /* Find the earliest node which _may_ be relevant to this one */
        this = jffs2_lookup_tn(&rii->tn_root, tn->fn->ofs);
-       if (!this) {
-               /* First addition to empty tree. $DEITY how I love the easy cases */
-               rb_link_node(&tn->rb, NULL, &rii->tn_root.rb_node);
-               rb_insert_color(&tn->rb, &rii->tn_root);
-               dbg_readinode("keep new frag\n");
-               return 0;
-       }
-
-       /* If the node is coincident with another at a lower address,
-          back up until the other node is found. It may be relevant */
-       while (tn->overlapped)
-               tn = tn_prev(tn);
+       if (this) {
+               /* If the node is coincident with another at a lower address,
+                  back up until the other node is found. It may be relevant */
+               while (this->overlapped)
+                       this = tn_prev(this);
 
-       dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole");
+               /* First node should never be marked overlapped */
+               BUG_ON(!this);
+               dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole");
+       }
 
        while (this) {
                if (this->fn->ofs > fn_end)
@@ -271,11 +274,11 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                                return 0;
                        } else {
                                /* Who cares if the new one is good; keep it for now anyway. */
+                               dbg_readinode("Like new node. Throw away old\n");
                                rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
-                               /* Same overlapping from in front and behind */
                                jffs2_kill_tn(c, this);
-                               dbg_readinode("Like new node. Throw away old\n");
-                               goto calc_overlaps;
+                               /* Same overlapping from in front and behind */
+                               return 0;
                        }
                }
                if (this->version < tn->version &&
@@ -287,12 +290,8 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                                jffs2_kill_tn(c, tn);
                                return 0;
                        }
-                       /* ... and is good. Kill 'this'... */
-                       rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
-                       jffs2_kill_tn(c, this);
-                       /* ... and any subsequent nodes which are also overlapped */
-                       this = tn_next(tn);
-                       while (this && this->fn->ofs + this->fn->size < fn_end) {
+                       /* ... and is good. Kill 'this' and any subsequent nodes which are also overlapped */
+                       while (this && this->fn->ofs + this->fn->size <= fn_end) {
                                struct jffs2_tmp_dnode_info *next = tn_next(this);
                                if (this->version < tn->version) {
                                        tn_erase(this, &rii->tn_root);
@@ -303,8 +302,8 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                                }
                                this = next;
                        }
-                       dbg_readinode("Done inserting new\n");
-                       goto calc_overlaps;
+                       dbg_readinode("Done killing overlapped nodes\n");
+                       continue;
                }
                if (this->version > tn->version &&
                    this->fn->ofs <= tn->fn->ofs &&
@@ -316,11 +315,10 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                                return 0;
                        }
                        /* ... but 'this' was bad. Replace it... */
-                       tn->overlapped = this->overlapped;
-                       rb_replace_node(&this->rb, &tn->rb, &rii->tn_root);
                        dbg_readinode("Bad CRC on old overlapping node. Kill it\n");
+                       tn_erase(this, &rii->tn_root);
                        jffs2_kill_tn(c, this);
-                       return 0;
+                       break;
                }
 
                this = tn_next(this);
@@ -331,7 +329,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
        {
                struct rb_node *parent;
                struct rb_node **link = &rii->tn_root.rb_node;
-               struct jffs2_tmp_dnode_info *insert_point;
+               struct jffs2_tmp_dnode_info *insert_point = NULL;
 
                while (*link) {
                        parent = *link;
@@ -348,7 +346,6 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                rb_insert_color(&tn->rb, &rii->tn_root);
        }
 
- calc_overlaps:
        /* If there's anything behind that overlaps us, note it */
        this = tn_prev(tn);
        if (this) {
@@ -1054,7 +1051,8 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
 
                case JFFS2_NODETYPE_DIRENT:
 
-                       if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent)) {
+                       if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent) &&
+                           len < sizeof(struct jffs2_raw_dirent)) {
                                err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf);
                                if (unlikely(err))
                                        goto free_out;
@@ -1068,7 +1066,8 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
 
                case JFFS2_NODETYPE_INODE:
 
-                       if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode)) {
+                       if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_inode) &&
+                           len < sizeof(struct jffs2_raw_inode)) {
                                err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf);
                                if (unlikely(err))
                                        goto free_out;
@@ -1081,7 +1080,8 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
                        break;
 
                default:
-                       if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node)) {
+                       if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node) &&
+                           len < sizeof(struct jffs2_unknown_node)) {
                                err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf);
                                if (unlikely(err))
                                        goto free_out;