[JFFS2] Split a large routine on several smaller.
authorArtem B. Bityutskiy <dedekind@infradead.org>
Fri, 15 Jul 2005 10:13:57 +0000 (11:13 +0100)
committerThomas Gleixner <tglx@mtd.linutronix.de>
Sun, 6 Nov 2005 14:40:55 +0000 (15:40 +0100)
Signed-off-by: Artem B. Bityutskiy <dedekind@infradead.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
fs/jffs2/nodelist.c

index 4991c348f6ec36ee82f2525e2ae4c86aeed82bfb..a04111530f4df0b44e039d68fdfd56a4664f335f 100644 (file)
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.c,v 1.98 2005/07/10 15:15:32 dedekind Exp $
+ * $Id: nodelist.c,v 1.99 2005/07/15 10:13:54 dedekind Exp $
  *
  */
 
@@ -137,6 +137,307 @@ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_r
        return NULL;
 }
 
+/*
+ * 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;
+ *         negative error code on failure.
+ */
+static inline int
+read_direntry(struct jffs2_sb_info *c,
+             struct jffs2_raw_node_ref *ref,
+             struct jffs2_raw_dirent *rd,
+             uint32_t read,
+             struct jffs2_full_dirent **fdp,
+             int32_t *latest_mctime,
+             uint32_t *mctime_ver)
+{
+       struct jffs2_full_dirent *fd;
+       
+       /* The direntry nodes are checked during the flash scanning */
+       BUG_ON(ref_flags(ref) == REF_UNCHECKED);
+       /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+       BUG_ON(ref_obsolete(ref));
+                       
+       /* Sanity check */
+       if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
+               printk(KERN_ERR "Error! Illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
+                      ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
+               return 1;
+       }
+       
+       fd = jffs2_alloc_full_dirent(rd->nsize + 1);
+       if (unlikely(!fd))
+               return -ENOMEM;
+
+       fd->raw = ref;
+       fd->version = je32_to_cpu(rd->version);
+       fd->ino = je32_to_cpu(rd->ino);
+       fd->type = rd->type;
+
+       /* Pick out the mctime of the latest dirent */
+       if(fd->version > *mctime_ver) {
+               *mctime_ver = fd->version;
+               *latest_mctime = je32_to_cpu(rd->mctime);
+       }
+
+       /* 
+        * Copy as much of the name as possible from the raw
+        * dirent we've already read from the flash.
+        */
+       if (read > sizeof(*rd))
+               memcpy(&fd->name[0], &rd->name[0],
+                      min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) ));
+               
+       /* Do we need to copy any more of the name directly from the flash? */
+       if (rd->nsize + sizeof(*rd) > read) {
+               /* FIXME: point() */
+               int err;
+               int already = read - sizeof(*rd);
+                       
+               err = jffs2_flash_read(c, (ref_offset(ref)) + read, 
+                               rd->nsize - already, &read, &fd->name[already]);
+               if (unlikely(read != rd->nsize - already) && likely(!err))
+                       return -EIO;
+                       
+               if (unlikely(err)) {
+                       printk(KERN_WARNING "Read remainder of name in jffs2_get_inode_nodes(): error %d\n", err);
+                       jffs2_free_full_dirent(fd);
+                       return -EIO;
+               }
+       }
+       
+       fd->nhash = full_name_hash(fd->name, rd->nsize);
+       fd->next = NULL;
+       fd->name[rd->nsize] = '\0';
+       
+       /*
+        * Wheee. We now have a complete jffs2_full_dirent structure, with
+        * the name in it and everything. Link it into the list 
+        */
+       D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino));
+
+       jffs2_add_fd_to_list(c, fd, fdp);
+
+       return 0;
+}
+
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * It is called every time an inode node is found.
+ *
+ * Returns: 0 on succes;
+ *         1 if the node should be marked obsolete;
+ *         negative error code on failure.
+ */
+static inline int
+read_dnode(struct jffs2_sb_info *c,
+          struct jffs2_raw_node_ref *ref,
+          struct jffs2_raw_inode *rd,
+          uint32_t read,
+          struct rb_root *tnp,
+          int32_t *latest_mctime,
+          uint32_t *mctime_ver)
+{
+       struct jffs2_eraseblock *jeb;
+       struct jffs2_tmp_dnode_info *tn;
+       
+       /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+       BUG_ON(ref_obsolete(ref));
+
+       /* If we've never checked the CRCs on this node, check them now */
+       if (ref_flags(ref) == REF_UNCHECKED) {
+               uint32_t crc, len;
+
+               crc = crc32(0, rd, sizeof(*rd) - 8);
+               if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+                       printk(KERN_WARNING "Header CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
+                                       ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
+                       return 1;
+               }
+               
+               /* 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)))) {
+                       printk(KERN_WARNING "Inode corrupted at %#08x, totlen %d, #ino  %d, version %d, "
+                               "isize %d, csize %d, dsize %d \n",
+                               ref_offset(ref),  je32_to_cpu(rd->totlen),  je32_to_cpu(rd->ino),
+                               je32_to_cpu(rd->version),  je32_to_cpu(rd->isize), 
+                               je32_to_cpu(rd->csize), je32_to_cpu(rd->dsize));
+                       return 1;
+               }
+
+               if (rd->compr != JFFS2_COMPR_ZERO && je32_to_cpu(rd->csize)) {
+                       unsigned char *buf = NULL;
+                       uint32_t pointed = 0;
+                       int err;
+#ifndef __ECOS
+                       if (c->mtd->point) {
+                               err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize),
+                                                    &read, &buf);
+                               if (unlikely(read < je32_to_cpu(rd->csize)) && likely(!err)) {
+                                       D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", read));
+                                       c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd),
+                                                       je32_to_cpu(rd->csize));
+                               } else if (unlikely(err)){
+                                       D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
+                               } else
+                                       pointed = 1; /* succefully pointed to device */
+                       }
+#endif                                 
+                       if(!pointed){
+                               buf = kmalloc(je32_to_cpu(rd->csize), GFP_KERNEL);
+                               if (!buf)
+                                       return -ENOMEM;
+                               
+                               err = jffs2_flash_read(c, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize),
+                                                       &read, buf);
+                               if (unlikely(read != je32_to_cpu(rd->csize)) && likely(!err))
+                                       err = -EIO;
+                               if (err) {
+                                       kfree(buf);
+                                       return err;
+                               }
+                       }
+                       crc = crc32(0, buf, je32_to_cpu(rd->csize));
+                       if(!pointed)
+                               kfree(buf);
+#ifndef __ECOS
+                       else
+                               c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(*rd), je32_to_cpu(rd->csize));
+#endif
+
+                       if (crc != je32_to_cpu(rd->data_crc)) {
+                               printk(KERN_NOTICE "Data CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
+                                      ref_offset(ref), je32_to_cpu(rd->data_crc), crc);
+                               return 1;
+                       }
+                       
+               }
+
+               /* Mark the node as having been checked and fix the accounting accordingly */
+               jeb = &c->blocks[ref->flash_offset / c->sector_size];
+               len = ref_totlen(c, jeb, ref);
+
+               spin_lock(&c->erase_completion_lock);
+               jeb->used_size += len;
+               jeb->unchecked_size -= len;
+               c->used_size += len;
+               c->unchecked_size -= len;
+
+               /* If node covers at least a whole page, or if it starts at the 
+                  beginning of a page and runs to the end of the file, or if 
+                  it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
+
+                  If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) 
+                  when the overlapping node(s) get added to the tree anyway. 
+               */
+               if ((je32_to_cpu(rd->dsize) >= PAGE_CACHE_SIZE) ||
+                   ( ((je32_to_cpu(rd->offset) & (PAGE_CACHE_SIZE-1))==0) &&
+                     (je32_to_cpu(rd->dsize) + je32_to_cpu(rd->offset) == je32_to_cpu(rd->isize)))) {
+                       D1(printk(KERN_DEBUG "Marking node at %#08x REF_PRISTINE\n", ref_offset(ref)));
+                       ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+               } else {
+                       D1(printk(KERN_DEBUG "Marking node at %#08x REF_NORMAL\n", ref_offset(ref)));
+                       ref->flash_offset = ref_offset(ref) | REF_NORMAL;
+               }
+               spin_unlock(&c->erase_completion_lock);
+       }
+
+       tn = jffs2_alloc_tmp_dnode_info();
+       if (!tn) {
+               D1(printk(KERN_DEBUG "alloc tn failed\n"));
+               return -ENOMEM;
+       }
+
+       tn->fn = jffs2_alloc_full_dnode();
+       if (!tn->fn) {
+               D1(printk(KERN_DEBUG "alloc fn failed\n"));
+               jffs2_free_tmp_dnode_info(tn);
+               return -ENOMEM;
+       }
+       
+       tn->version = je32_to_cpu(rd->version);
+       tn->fn->ofs = je32_to_cpu(rd->offset);
+       tn->fn->raw = ref;
+       
+       /* There was a bug where we wrote hole nodes out with
+          csize/dsize swapped. Deal with it */
+       if (rd->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(rd->dsize) && je32_to_cpu(rd->csize))
+               tn->fn->size = je32_to_cpu(rd->csize);
+       else // normal case...
+               tn->fn->size = je32_to_cpu(rd->dsize);
+
+       D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %#04x, dsize %#04x\n",
+                 ref_offset(ref), je32_to_cpu(rd->version),
+                 je32_to_cpu(rd->offset), je32_to_cpu(rd->dsize)));
+       
+       jffs2_add_tn_to_tree(tn, tnp);
+
+       return 0;
+}
+
+/*
+ * Helper function for jffs2_get_inode_nodes().
+ * It is called every time an unknown node is found.
+ *
+ * Returns: 0 on succes;
+ *         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,
+            uint32_t read)
+{
+       /* We don't mark unknown nodes as REF_UNCHECKED */
+       BUG_ON(ref_flags(ref) == REF_UNCHECKED);
+       
+       un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
+
+       if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) {
+
+               /* Hmmm. This should have been caught at scan time. */
+               printk(KERN_WARNING "Warning! Node header CRC failed at %#08x. "
+                               "But it must have been OK earlier.\n", ref_offset(ref));
+               D1(printk(KERN_DEBUG "Node was: { %#04x, %#04x, %#08x, %#08x }\n", 
+                       je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
+                       je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)));
+               return 1;
+       } else {
+               switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
+
+               case JFFS2_FEATURE_INCOMPAT:
+                       printk(KERN_NOTICE "Unknown INCOMPAT nodetype %#04X at %#08x\n",
+                                       je16_to_cpu(un->nodetype), ref_offset(ref));
+                       /* EEP */
+                       BUG();
+                       break;
+
+               case JFFS2_FEATURE_ROCOMPAT:
+                       printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %#04X at %#08x\n",
+                                       je16_to_cpu(un->nodetype), ref_offset(ref));
+                       BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
+                       break;
+
+               case JFFS2_FEATURE_RWCOMPAT_COPY:
+                       printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
+                                       je16_to_cpu(un->nodetype), ref_offset(ref));
+                       break;
+
+               case JFFS2_FEATURE_RWCOMPAT_DELETE:
+                       printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
+                                       je16_to_cpu(un->nodetype), ref_offset(ref));
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
    with this ino, returning the former in order of version */
 
@@ -146,9 +447,8 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                          uint32_t *mctime_ver)
 {
        struct jffs2_raw_node_ref *ref, *valid_ref;
-       struct jffs2_tmp_dnode_info *tn;
        struct rb_root ret_tn = RB_ROOT;
-       struct jffs2_full_dirent *fd, *ret_fd = NULL;
+       struct jffs2_full_dirent *ret_fd = NULL;
        union jffs2_node_union node;
        size_t retlen;
        int err;
@@ -186,292 +486,67 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                        goto free_out;
                }
                        
-
-                       /* Check we've managed to read at least the common node header */
-               if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
-                       printk(KERN_WARNING "short read in get_inode_nodes()\n");
-                       err = -EIO;
-                       goto free_out;
-               }
-                       
                switch (je16_to_cpu(node.u.nodetype)) {
+                       
                case JFFS2_NODETYPE_DIRENT:
                        D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
-                       if (ref_flags(ref) == REF_UNCHECKED) {
-                               printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
-                               BUG();
-                       }
+                       
                        if (retlen < sizeof(node.d)) {
-                               printk(KERN_WARNING "short read in get_inode_nodes()\n");
+                               printk(KERN_WARNING "Warning! Short read dirent at %#08x\n", ref_offset(ref));
                                err = -EIO;
                                goto free_out;
                        }
-                       /* sanity check */
-                       if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) {
-                               printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n",
-                                      ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen));
+
+                       err = read_direntry(c, ref, &node.d, retlen, &ret_fd, latest_mctime, mctime_ver);
+                       if (err == 1) {
                                jffs2_mark_node_obsolete(c, ref);
-                               spin_lock(&c->erase_completion_lock);
-                               continue;
-                       }
+                               break;
+                       } else if (unlikely(err))
+                               goto free_out;
+                       
                        if (je32_to_cpu(node.d.version) > *highest_version)
                                *highest_version = je32_to_cpu(node.d.version);
-                       if (ref_obsolete(ref)) {
-                               /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
-                               printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
-                                      ref_offset(ref));
-                               BUG();
-                       }
-                       
-                       fd = jffs2_alloc_full_dirent(node.d.nsize+1);
-                       if (!fd) {
-                               err = -ENOMEM;
-                               goto free_out;
-                       }
-                       fd->raw = ref;
-                       fd->version = je32_to_cpu(node.d.version);
-                       fd->ino = je32_to_cpu(node.d.ino);
-                       fd->type = node.d.type;
-
-                       /* Pick out the mctime of the latest dirent */
-                       if(fd->version > *mctime_ver) {
-                               *mctime_ver = fd->version;
-                               *latest_mctime = je32_to_cpu(node.d.mctime);
-                       }
 
-                       /* memcpy as much of the name as possible from the raw
-                          dirent we've already read from the flash
-                       */
-                       if (retlen > sizeof(struct jffs2_raw_dirent))
-                               memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
-                               
-                       /* Do we need to copy any more of the name directly
-                          from the flash?
-                       */
-                       if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
-                               /* FIXME: point() */
-                               int already = retlen - sizeof(struct jffs2_raw_dirent);
-                                       
-                               err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, 
-                                                  node.d.nsize - already, &retlen, &fd->name[already]);
-                               if (!err && retlen != node.d.nsize - already)
-                                       err = -EIO;
-                                       
-                               if (err) {
-                                       printk(KERN_WARNING "Read remainder of name in jffs2_get_inode_nodes(): error %d\n", err);
-                                       jffs2_free_full_dirent(fd);
-                                       goto free_out;
-                               }
-                       }
-                       fd->nhash = full_name_hash(fd->name, node.d.nsize);
-                       fd->next = NULL;
-                       fd->name[node.d.nsize] = '\0';
-                               /* Wheee. We now have a complete jffs2_full_dirent structure, with
-                                  the name in it and everything. Link it into the list 
-                               */
-                       D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino));
-                       jffs2_add_fd_to_list(c, fd, &ret_fd);
                        break;
 
                case JFFS2_NODETYPE_INODE:
                        D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
+                       
                        if (retlen < sizeof(node.i)) {
-                               printk(KERN_WARNING "read too short for dnode\n");
+                               printk(KERN_WARNING "Warning! Short read dnode at %#08x\n", ref_offset(ref));
                                err = -EIO;
                                goto free_out;
                        }
-                       if (je32_to_cpu(node.i.version) > *highest_version)
-                               *highest_version = je32_to_cpu(node.i.version);
-                       D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
-
-                       if (ref_obsolete(ref)) {
-                               /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
-                               printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
-                                      ref_offset(ref));
-                               BUG();
-                       }
-
-                       /* If we've never checked the CRCs on this node, check them now. */
-                       if (ref_flags(ref) == REF_UNCHECKED) {
-                               uint32_t crc, len;
-                               struct jffs2_eraseblock *jeb;
-
-                               crc = crc32(0, &node, sizeof(node.i)-8);
-                               if (crc != je32_to_cpu(node.i.node_crc)) {
-                                       printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                                              ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
-                                       jffs2_mark_node_obsolete(c, ref);
-                                       spin_lock(&c->erase_completion_lock);
-                                       continue;
-                               }
-                               
-                               /* sanity checks */
-                               if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) ||
-                                    PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) {
-                                       printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino  %d, version %d, isize %d, csize %d, dsize %d \n",
-                                               ref_offset(ref),  je32_to_cpu(node.i.totlen),  je32_to_cpu(node.i.ino),
-                                               je32_to_cpu(node.i.version),  je32_to_cpu(node.i.isize), 
-                                               je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize));
-                                       jffs2_mark_node_obsolete(c, ref);
-                                       spin_lock(&c->erase_completion_lock);
-                                       continue;
-                               }
 
-                               if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
-                                       unsigned char *buf=NULL;
-                                       uint32_t pointed = 0;
-#ifndef __ECOS
-                                       if (c->mtd->point) {
-                                               err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
-                                                                    &retlen, &buf);
-                                               if (!err && retlen < je32_to_cpu(node.i.csize)) {
-                                                       D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
-                                                       c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
-                                               } else if (err){
-                                                       D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
-                                               } else
-                                                       pointed = 1; /* succefully pointed to device */
-                                       }
-#endif                                 
-                                       if(!pointed){
-                                               buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
-                                               if (!buf)
-                                                       return -ENOMEM;
-                                               
-                                               err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
-                                                                      &retlen, buf);
-                                               if (!err && retlen != je32_to_cpu(node.i.csize))
-                                                       err = -EIO;
-                                               if (err) {
-                                                       kfree(buf);
-                                                       return err;
-                                               }
-                                       }
-                                       crc = crc32(0, buf, je32_to_cpu(node.i.csize));
-                                       if(!pointed)
-                                               kfree(buf);
-#ifndef __ECOS
-                                       else
-                                               c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
-#endif
-
-                                       if (crc != je32_to_cpu(node.i.data_crc)) {
-                                               printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-                                                      ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
-                                               jffs2_mark_node_obsolete(c, ref);
-                                               spin_lock(&c->erase_completion_lock);
-                                               continue;
-                                       }
-                                       
-                               }
-
-                               /* Mark the node as having been checked and fix the accounting accordingly */
-                               spin_lock(&c->erase_completion_lock);
-                               jeb = &c->blocks[ref->flash_offset / c->sector_size];
-                               len = ref_totlen(c, jeb, ref);
-
-                               jeb->used_size += len;
-                               jeb->unchecked_size -= len;
-                               c->used_size += len;
-                               c->unchecked_size -= len;
-
-                               /* If node covers at least a whole page, or if it starts at the 
-                                  beginning of a page and runs to the end of the file, or if 
-                                  it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
-
-                                  If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) 
-                                  when the overlapping node(s) get added to the tree anyway. 
-                               */
-                               if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
-                                   ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
-                                     (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) ==  je32_to_cpu(node.i.isize)))) {
-                                       D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
-                                       ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
-                               } else {
-                                       D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
-                                       ref->flash_offset = ref_offset(ref) | REF_NORMAL;
-                               }
-                               spin_unlock(&c->erase_completion_lock);
-                       }
-
-                       tn = jffs2_alloc_tmp_dnode_info();
-                       if (!tn) {
-                               D1(printk(KERN_DEBUG "alloc tn failed\n"));
-                               err = -ENOMEM;
+                       err = read_dnode(c, ref, &node.i, retlen, &ret_tn, latest_mctime, mctime_ver);
+                       if (err == 1) {
+                               jffs2_mark_node_obsolete(c, ref);
+                               break;
+                       } else if (unlikely(err))
                                goto free_out;
-                       }
 
-                       tn->fn = jffs2_alloc_full_dnode();
-                       if (!tn->fn) {
-                               D1(printk(KERN_DEBUG "alloc fn failed\n"));
-                               err = -ENOMEM;
-                               jffs2_free_tmp_dnode_info(tn);
-                               goto free_out;
-                       }
-                       tn->version = je32_to_cpu(node.i.version);
-                       tn->fn->ofs = je32_to_cpu(node.i.offset);
-                       /* There was a bug where we wrote hole nodes out with
-                          csize/dsize swapped. Deal with it */
-                       if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
-                               tn->fn->size = je32_to_cpu(node.i.csize);
-                       else // normal case...
-                               tn->fn->size = je32_to_cpu(node.i.dsize);
-                       tn->fn->raw = ref;
-                       D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
-                                 ref_offset(ref), je32_to_cpu(node.i.version),
-                                 je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
-                       jffs2_add_tn_to_tree(tn, &ret_tn);
+                       if (je32_to_cpu(node.i.version) > *highest_version)
+                               *highest_version = je32_to_cpu(node.i.version);
+                       
+                       D1(printk(KERN_DEBUG "version %d, highest_version now %d\n",
+                                       je32_to_cpu(node.i.version), *highest_version));
+
                        break;
 
                default:
-                       if (ref_flags(ref) == REF_UNCHECKED) {
-                               struct jffs2_eraseblock *jeb;
-                               uint32_t len;
-
-                               printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
-                                      je16_to_cpu(node.u.nodetype), ref_offset(ref));
-
-                               /* Mark the node as having been checked and fix the accounting accordingly */
-                               spin_lock(&c->erase_completion_lock);
-                               jeb = &c->blocks[ref->flash_offset / c->sector_size];
-                               len = ref_totlen(c, jeb, ref);
-
-                               jeb->used_size += len;
-                               jeb->unchecked_size -= len;
-                               c->used_size += len;
-                               c->unchecked_size -= len;
-
-                               mark_ref_normal(ref);
-                               spin_unlock(&c->erase_completion_lock);
+                       /* Check we've managed to read at least the common node header */
+                       if (retlen < sizeof(struct jffs2_unknown_node)) {
+                               printk(KERN_WARNING "Warning! Short read unknown node at %#08x\n",
+                                               ref_offset(ref));
+                               return -EIO;
                        }
-                       node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
-                       if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
-                               /* Hmmm. This should have been caught at scan time. */
-                               printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
-                                      ref_offset(ref));
-                               printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", 
-                                      je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
-                                      je32_to_cpu(node.u.hdr_crc));
-                               jffs2_mark_node_obsolete(c, ref);
-                       } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
-                       case JFFS2_FEATURE_INCOMPAT:
-                               printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
-                               /* EEP */
-                               BUG();
-                               break;
-                       case JFFS2_FEATURE_ROCOMPAT:
-                               printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
-                               if (!(c->flags & JFFS2_SB_FLAG_RO))
-                                       BUG();
-                               break;
-                       case JFFS2_FEATURE_RWCOMPAT_COPY:
-                               printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
-                               break;
-                       case JFFS2_FEATURE_RWCOMPAT_DELETE:
-                               printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+
+                       err = read_unknown(c, ref, &node.u, retlen);
+                       if (err == 1) {
                                jffs2_mark_node_obsolete(c, ref);
                                break;
-                       }
+                       } else if (unlikely(err))
+                               goto free_out;
 
                }
                spin_lock(&c->erase_completion_lock);