Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh...
[safe/jmp/linux-2.6] / fs / jffs2 / readinode.c
index b09fa23..d32ee94 100644 (file)
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: readinode.c,v 1.143 2005/11/07 11:14:41 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -39,23 +37,24 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
 
        BUG_ON(tn->csize == 0);
 
-       if (!jffs2_is_writebuffered(c))
-               goto adj_acc;
-
        /* Calculate how many bytes were already checked */
        ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode);
-       len = ofs % c->wbuf_pagesize;
-       if (likely(len))
-               len = c->wbuf_pagesize - len;
-
-       if (len >= tn->csize) {
-               dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n",
-                       ref_offset(ref), tn->csize, ofs);
-               goto adj_acc;
-       }
+       len = tn->csize;
 
-       ofs += len;
-       len = tn->csize - len;
+       if (jffs2_is_writebuffered(c)) {
+               int adj = ofs % c->wbuf_pagesize;
+               if (likely(adj))
+                       adj = c->wbuf_pagesize - adj;
+
+               if (adj >= tn->csize) {
+                       dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n",
+                                     ref_offset(ref), tn->csize, ofs);
+                       goto adj_acc;
+               }
+
+               ofs += adj;
+               len -= adj;
+       }
 
        dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n",
                ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len);
@@ -64,10 +63,11 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
        /* TODO: instead, incapsulate point() stuff to jffs2_flash_read(),
         * adding and jffs2_flash_read_end() interface. */
        if (c->mtd->point) {
-               err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
-               if (!err && retlen < tn->csize) {
+               err = c->mtd->point(c->mtd, ofs, len, &retlen,
+                                   (void **)&buffer, NULL);
+               if (!err && retlen < len) {
                        JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
-                       c->mtd->unpoint(c->mtd, buffer, ofs, len);
+                       c->mtd->unpoint(c->mtd, ofs, retlen);
                } else if (err)
                        JFFS2_WARNING("MTD point failed: error code %d.\n", err);
                else
@@ -101,12 +101,12 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
                kfree(buffer);
 #ifndef __ECOS
        else
-               c->mtd->unpoint(c->mtd, buffer, ofs, len);
+               c->mtd->unpoint(c->mtd, ofs, len);
 #endif
 
        if (crc != tn->data_crc) {
                JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n",
-                       ofs, tn->data_crc, crc);
+                            ref_offset(ref), tn->data_crc, crc);
                return 1;
        }
 
@@ -137,7 +137,7 @@ free_out:
                kfree(buffer);
 #ifndef __ECOS
        else
-               c->mtd->unpoint(c->mtd, buffer, ofs, len);
+               c->mtd->unpoint(c->mtd, ofs, len);
 #endif
        return err;
 }
@@ -212,18 +212,17 @@ static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *
  * offset, and the one with the smallest length will come first in the
  * ordering.
  *
- * Returns 0 if the node was inserted
- *         1 if the node is obsolete (because we can't mark it so yet)
- *         < 0 an if error occurred
+ * Returns 0 if the node was handled (including marking it obsolete)
+ *      < 0 an if error occurred
  */
 static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                                struct jffs2_readinode_info *rii,
                                struct jffs2_tmp_dnode_info *tn)
 {
        uint32_t fn_end = tn->fn->ofs + tn->fn->size;
-       struct jffs2_tmp_dnode_info *insert_point = NULL, *this;
+       struct jffs2_tmp_dnode_info *this, *ptn;
 
-       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.
@@ -231,9 +230,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);
@@ -242,24 +248,24 @@ 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 (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) {
+                       ptn = tn_prev(this);
+                       if (!ptn) {
+                               /*
+                                * We killed a node which set the overlapped
+                                * flags during the scan. Fix it up.
+                                */
+                               this->overlapped = 0;
+                               break;
+                       }
+                       this = ptn;
+               }
+               dbg_readinode("'this' found %#04x-%#04x (%s)\n", this->fn->ofs, this->fn->ofs + this->fn->size, this->fn ? "data" : "hole");
        }
 
-       /* If we add a new node it'll be somewhere under here. */
-       insert_point = 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 (tn->overlapped)
-               tn = tn_prev(tn);
-
-       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)
                        break;
@@ -276,11 +282,10 @@ 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 */
-                               tn->overlapped = this->overlapped;
                                jffs2_kill_tn(c, this);
-                               dbg_readinode("Like new node. Throw away old\n");
+                               /* Same overlapping from in front and behind */
                                return 0;
                        }
                }
@@ -293,13 +298,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);
-                       tn->overlapped = this->overlapped;
-                       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);
@@ -310,8 +310,8 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                                }
                                this = next;
                        }
-                       dbg_readinode("Done inserting new\n");
-                       return 0;
+                       dbg_readinode("Done killing overlapped nodes\n");
+                       continue;
                }
                if (this->version > tn->version &&
                    this->fn->ofs <= tn->fn->ofs &&
@@ -323,29 +323,21 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                                return 0;
                        }
                        /* ... but 'this' was bad. Replace it... */
-                       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;
                }
-               /* We want to be inserted under the last node which is
-                  either at a lower offset _or_ has a smaller range */
-               if (this->fn->ofs < tn->fn->ofs ||
-                   (this->fn->ofs == tn->fn->ofs &&
-                    this->fn->size <= tn->fn->size))
-                       insert_point = this;
 
                this = tn_next(this);
        }
-       dbg_readinode("insert_point %p, ver %d, 0x%x-0x%x, ov %d\n",
-                     insert_point, insert_point->version, insert_point->fn->ofs,
-                     insert_point->fn->ofs+insert_point->fn->size,
-                     insert_point->overlapped);
+
        /* We neither completely obsoleted nor were completely
-          obsoleted by an earlier node. Insert under insert_point */
+          obsoleted by an earlier node. Insert into the tree */
        {
-               struct rb_node *parent = &insert_point->rb;
-               struct rb_node **link = &parent;
+               struct rb_node *parent;
+               struct rb_node **link = &rii->tn_root.rb_node;
+               struct jffs2_tmp_dnode_info *insert_point = NULL;
 
                while (*link) {
                        parent = *link;
@@ -361,6 +353,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                rb_link_node(&tn->rb, &insert_point->rb, link);
                rb_insert_color(&tn->rb, &rii->tn_root);
        }
+
        /* If there's anything behind that overlaps us, note it */
        this = tn_prev(tn);
        if (this) {
@@ -374,7 +367,17 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
                        }
                        if (!this->overlapped)
                                break;
-                       this = tn_prev(this);
+
+                       ptn = tn_prev(this);
+                       if (!ptn) {
+                               /*
+                                * We killed a node which set the overlapped
+                                * flags during the scan. Fix it up.
+                                */
+                               this->overlapped = 0;
+                               break;
+                       }
+                       this = ptn;
                }
        }
 
@@ -459,7 +462,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
        this = tn_last(&rii->tn_root);
        while (this) {
                dbg_readinode("tn %p ver %d range 0x%x-0x%x ov %d\n", this, this->version, this->fn->ofs,
-                            this->fn->ofs+this->fn->size, this->overlapped);
+                             this->fn->ofs+this->fn->size, this->overlapped);
                this = tn_prev(this);
        }
 #endif
@@ -470,8 +473,15 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
                eat_last(&rii->tn_root, &last->rb);
                ver_insert(&ver_root, last);
 
-               if (unlikely(last->overlapped))
-                       continue;
+               if (unlikely(last->overlapped)) {
+                       if (pen)
+                               continue;
+                       /*
+                        * We killed a node which set the overlapped
+                        * flags during the scan. Fix it up.
+                        */
+                       last->overlapped = 0;
+               }
 
                /* Now we have a bunch of nodes in reverse version
                   order, in the tree at ver_root. Most of the time,
@@ -485,7 +495,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
                        vers_next = tn_prev(this);
                        eat_last(&ver_root, &this->rb);
                        if (check_tn_node(c, this)) {
-                               dbg_readinode("node ver %x, 0x%x-0x%x failed CRC\n",
+                               dbg_readinode("node ver %d, 0x%x-0x%x failed CRC\n",
                                             this->version, this->fn->ofs,
                                             this->fn->ofs+this->fn->size);
                                jffs2_kill_tn(c, this);
@@ -498,7 +508,7 @@ static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,
                                        high_ver = this->version;
                                        rii->latest_ref = this->fn->raw;
                                }
-                               dbg_readinode("Add %p (v %x, 0x%x-0x%x, ov %d) to fragtree\n",
+                               dbg_readinode("Add %p (v %d, 0x%x-0x%x, ov %d) to fragtree\n",
                                             this, this->version, this->fn->ofs,
                                             this->fn->ofs+this->fn->size, this->overlapped);
 
@@ -557,7 +567,7 @@ static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
                        else BUG();
                }
        }
-       list->rb_node = NULL;
+       *list = RB_ROOT;
 }
 
 static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
@@ -587,8 +597,7 @@ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_r
  * Helper function for jffs2_get_inode_nodes().
  * It is called every time an directory entry node is found.
  *
- * Returns: 0 on succes;
- *         1 if the node should be marked obsolete;
+ * Returns: 0 on success;
  *         negative error code on failure.
  */
 static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
@@ -630,7 +639,7 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r
                jeb->unchecked_size -= len;
                c->used_size += len;
                c->unchecked_size -= len;
-               ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+               ref->flash_offset = ref_offset(ref) | dirent_node_state(rd);
                spin_unlock(&c->erase_completion_lock);
        }
 
@@ -695,8 +704,7 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r
  * Helper function for jffs2_get_inode_nodes().
  * It is called every time an inode node is found.
  *
- * Returns: 0 on success;
- *         1 if the node should be marked obsolete;
+ * Returns: 0 on success (possibly after marking a bad node obsolete);
  *         negative error code on failure.
  */
 static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
@@ -705,7 +713,7 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 {
        struct jffs2_tmp_dnode_info *tn;
        uint32_t len, csize;
-       int ret = 1;
+       int ret = 0;
        uint32_t crc;
 
        /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
@@ -734,8 +742,9 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
                /* Sanity checks */
                if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
                    unlikely(PAD(je32_to_cpu(rd->csize) + sizeof(*rd)) != PAD(je32_to_cpu(rd->totlen)))) {
-                               JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref));
-                               jffs2_dbg_dump_node(c, ref_offset(ref));
+                       JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref));
+                       jffs2_dbg_dump_node(c, ref_offset(ref));
+                       jffs2_mark_node_obsolete(c, ref);
                        goto free_out;
                }
 
@@ -758,7 +767,7 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
                         * are not obsolete.
                         *
                         * Of course, this optimization only makes sense in case
-                        * of NAND flashes (or other flashes whith
+                        * of NAND flashes (or other flashes with
                         * !jffs2_can_mark_obsolete()), since on NOR flashes
                         * nodes are marked obsolete physically.
                         *
@@ -790,6 +799,7 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
                        if (len >= csize && unlikely(tn->partial_crc != je32_to_cpu(rd->data_crc))) {
                                JFFS2_NOTICE("wrong data CRC in data node at 0x%08x: read %#08x, calculated %#08x.\n",
                                        ref_offset(ref), tn->partial_crc, je32_to_cpu(rd->data_crc));
+                               jffs2_mark_node_obsolete(c, ref);
                                goto free_out;
                        }
 
@@ -840,8 +850,9 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
        else // normal case...
                tn->fn->size = je32_to_cpu(rd->dsize);
 
-       dbg_readinode("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n",
-                 ref_offset(ref), je32_to_cpu(rd->version), je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize);
+       dbg_readinode2("dnode @%08x: ver %u, offset %#04x, dsize %#04x, csize %#04x\n",
+                      ref_offset(ref), je32_to_cpu(rd->version),
+                      je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize), csize);
 
        ret = jffs2_add_tn_to_tree(c, rii, tn);
 
@@ -851,13 +862,13 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
                jffs2_free_tmp_dnode_info(tn);
                return ret;
        }
-#ifdef JFFS2_DBG_READINODE_MESSAGES
-       dbg_readinode("After adding ver %d:\n", tn->version);
+#ifdef JFFS2_DBG_READINODE2_MESSAGES
+       dbg_readinode2("After adding ver %d:\n", je32_to_cpu(rd->version));
        tn = tn_first(&rii->tn_root);
        while (tn) {
-               dbg_readinode("%p: v %d r 0x%x-0x%x ov %d\n",
-                            tn, tn->version, tn->fn->ofs,
-                            tn->fn->ofs+tn->fn->size, tn->overlapped);
+               dbg_readinode2("%p: v %d r 0x%x-0x%x ov %d\n",
+                              tn, tn->version, tn->fn->ofs,
+                              tn->fn->ofs+tn->fn->size, tn->overlapped);
                tn = tn_next(tn);
        }
 #endif
@@ -869,7 +880,6 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
  * It is called every time an unknown node is found.
  *
  * Returns: 0 on success;
- *         1 if the node should be marked obsolete;
  *         negative error code on failure.
  */
 static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un)
@@ -879,8 +889,8 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
                JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n",
                            ref_offset(ref));
                JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n",
-                            je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
-                            je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
+                           je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
+                           je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
                jffs2_mark_node_obsolete(c, ref);
                return 0;
        }
@@ -921,7 +931,7 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
  * Helper function for jffs2_get_inode_nodes().
  * The function detects whether more data should be read and reads it if yes.
  *
- * Returns: 0 on succes;
+ * Returns: 0 on success;
  *         negative error code on failure.
  */
 static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
@@ -1066,7 +1076,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;
@@ -1080,7 +1091,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;
@@ -1093,17 +1105,15 @@ 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;
                        }
 
                        err = read_unknown(c, ref, &node->u);
-                       if (err == 1) {
-                               jffs2_mark_node_obsolete(c, ref);
-                               break;
-                       } else if (unlikely(err))
+                       if (unlikely(err))
                                goto free_out;
 
                }
@@ -1134,11 +1144,12 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                                        struct jffs2_raw_inode *latest_node)
 {
        struct jffs2_readinode_info rii;
-       uint32_t crc;
+       uint32_t crc, new_size;
        size_t retlen;
        int ret;
 
-       dbg_readinode("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink);
+       dbg_readinode("ino #%u pino/nlink is %d\n", f->inocache->ino,
+                     f->inocache->pino_nlink);
 
        memset(&rii, 0, sizeof(rii));
 
@@ -1209,7 +1220,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",
                        ret, retlen, sizeof(*latest_node));
                /* FIXME: If this fails, there seems to be a memory leak. Find it. */
-               up(&f->sem);
+               mutex_unlock(&f->sem);
                jffs2_do_clear_inode(c, f);
                return ret?ret:-EIO;
        }
@@ -1218,7 +1229,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
        if (crc != je32_to_cpu(latest_node->node_crc)) {
                JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
                        f->inocache->ino, ref_offset(rii.latest_ref));
-               up(&f->sem);
+               mutex_unlock(&f->sem);
                jffs2_do_clear_inode(c, f);
                return -EIO;
        }
@@ -1235,7 +1246,12 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
 
        case S_IFREG:
                /* If it was a regular file, truncate it to the latest node's isize */
-               jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));
+               new_size = jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));
+               if (new_size != je32_to_cpu(latest_node->isize)) {
+                       JFFS2_WARNING("Truncating ino #%u to %d bytes failed because it only had %d bytes to start with!\n",
+                                     f->inocache->ino, je32_to_cpu(latest_node->isize), new_size);
+                       latest_node->isize = cpu_to_je32(new_size);
+               }
                break;
 
        case S_IFLNK:
@@ -1253,7 +1269,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                        f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
                        if (!f->target) {
                                JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize));
-                               up(&f->sem);
+                               mutex_unlock(&f->sem);
                                jffs2_do_clear_inode(c, f);
                                return -ENOMEM;
                        }
@@ -1266,9 +1282,9 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                                        ret = -EIO;
                                kfree(f->target);
                                f->target = NULL;
-                               up(&f->sem);
+                               mutex_unlock(&f->sem);
                                jffs2_do_clear_inode(c, f);
-                               return -ret;
+                               return ret;
                        }
 
                        f->target[je32_to_cpu(latest_node->csize)] = '\0';
@@ -1284,14 +1300,14 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                if (f->metadata) {
                        JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
                               f->inocache->ino, jemode_to_cpu(latest_node->mode));
-                       up(&f->sem);
+                       mutex_unlock(&f->sem);
                        jffs2_do_clear_inode(c, f);
                        return -EIO;
                }
                if (!frag_first(&f->fragtree)) {
                        JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
                               f->inocache->ino, jemode_to_cpu(latest_node->mode));
-                       up(&f->sem);
+                       mutex_unlock(&f->sem);
                        jffs2_do_clear_inode(c, f);
                        return -EIO;
                }
@@ -1300,7 +1316,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                        JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n",
                               f->inocache->ino, jemode_to_cpu(latest_node->mode));
                        /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
-                       up(&f->sem);
+                       mutex_unlock(&f->sem);
                        jffs2_do_clear_inode(c, f);
                        return -EIO;
                }
@@ -1368,7 +1384,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                }
                dbg_readinode("creating inocache for root inode\n");
                memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
-               f->inocache->ino = f->inocache->nlink = 1;
+               f->inocache->ino = f->inocache->pino_nlink = 1;
                f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
                f->inocache->state = INO_STATE_READING;
                jffs2_add_ino_cache(c, f->inocache);
@@ -1390,12 +1406,13 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
        if (!f)
                return -ENOMEM;
 
-       init_MUTEX_LOCKED(&f->sem);
+       mutex_init(&f->sem);
+       mutex_lock(&f->sem);
        f->inocache = ic;
 
        ret = jffs2_do_read_inode_internal(c, f, &n);
        if (!ret) {
-               up(&f->sem);
+               mutex_unlock(&f->sem);
                jffs2_do_clear_inode(c, f);
        }
        kfree (f);
@@ -1407,10 +1424,9 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
        struct jffs2_full_dirent *fd, *fds;
        int deleted;
 
-       jffs2_clear_acl(f);
        jffs2_xattr_delete_inode(c, f->inocache);
-       down(&f->sem);
-       deleted = f->inocache && !f->inocache->nlink;
+       mutex_lock(&f->sem);
+       deleted = f->inocache && !f->inocache->pino_nlink;
 
        if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
                jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
@@ -1441,5 +1457,5 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
                        jffs2_del_ino_cache(c, f->inocache);
        }
 
-       up(&f->sem);
+       mutex_unlock(&f->sem);
 }