[JFFS2] Correctly handle wasted space before summary node.
[powerpc.git] / fs / jffs2 / nodelist.c
index 9c57573..0e82979 100644 (file)
@@ -438,7 +438,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
        if (c->mtd->point) {
                err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
                if (!err && retlen < tn->csize) {
-                       JFFS2_WARNING("MTD point returned len too short: %u instead of %u.\n", retlen, tn->csize);
+                       JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
                        c->mtd->unpoint(c->mtd, buffer, ofs, len);
                } else if (err)
                        JFFS2_WARNING("MTD point failed: error code %d.\n", err);
@@ -461,7 +461,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
                }
 
                if (retlen != len) {
-                       JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ofs, retlen, len);
+                       JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len);
                        err = -EIO;
                        goto free_out;
                }
@@ -953,13 +953,19 @@ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
 
        for (i=0; i<c->nr_blocks; i++) {
                this = c->blocks[i].first_node;
-               while(this) {
+               while (this) {
                        next = this->next_phys;
-                       jffs2_free_raw_node_ref(this);
+                       __jffs2_free_raw_node_ref(this);
                        this = next;
                }
                c->blocks[i].first_node = c->blocks[i].last_node = NULL;
        }
+       this = c->refs;
+       while (this) {
+               next = this->next_in_ino;
+               __jffs2_free_raw_node_ref(this);
+               this = next;
+       }
 }
 
 struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
@@ -1046,3 +1052,166 @@ void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
                cond_resched();
        }
 }
+
+struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
+                                              struct jffs2_eraseblock *jeb,
+                                              uint32_t ofs, uint32_t len,
+                                              struct jffs2_inode_cache *ic)
+{
+       struct jffs2_raw_node_ref *ref;
+
+       /* These will be preallocated _very_ shortly. */
+       ref = c->refs;
+       if (!c->refs) {
+               JFFS2_WARNING("Using non-preallocated refs!\n");
+               ref = __jffs2_alloc_raw_node_ref();
+               BUG_ON(!ref);
+               WARN_ON(1);
+       } else {
+               c->refs = ref->next_in_ino;
+       }
+
+       ref->next_phys = NULL;
+       ref->flash_offset = ofs;
+
+       if (!jeb->first_node)
+               jeb->first_node = ref;
+       if (jeb->last_node) {
+               jeb->last_node->next_phys = ref;
+#ifdef TEST_TOTLEN
+               if (ref_offset(jeb->last_node) + jeb->last_node->__totlen != ref_offset(ref)) {
+                       printk(KERN_CRIT "Adding new ref %p at (0x%08x-0x%08x) not immediately after previous (0x%08x-0x%08x)\n",
+                              ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
+                              ref_offset(jeb->last_node), ref_offset(jeb->last_node)+jeb->last_node->__totlen);
+                       WARN_ON(1);
+               }
+#endif
+       }
+       jeb->last_node = ref;
+
+       if (ic) {
+               ref->next_in_ino = ic->nodes;
+               ic->nodes = ref;
+       } else {
+               ref->next_in_ino = NULL;
+       }
+
+       switch(ref_flags(ref)) {
+       case REF_UNCHECKED:
+               c->unchecked_size += len;
+               jeb->unchecked_size += len;
+               break;
+
+       case REF_NORMAL:
+       case REF_PRISTINE:
+               c->used_size += len;
+               jeb->used_size += len;
+               break;
+
+       case REF_OBSOLETE:
+               c->dirty_size += len;
+               jeb->dirty_size += len;
+               break;
+       }
+       c->free_size -= len;
+       jeb->free_size -= len;
+
+#ifdef TEST_TOTLEN
+       /* Set (and test) __totlen field... for now */
+       ref->__totlen = len;
+       ref_totlen(c, jeb, ref);
+#endif
+       return ref;
+}
+
+/* No locking, no reservation of 'ref'. Do not use on a live file system */
+int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+                          uint32_t size)
+{
+       if (!size)
+               return 0;
+       if (size > c->sector_size - jeb->used_size) {
+               printk(KERN_CRIT "Dirty space 0x%x larger then used_size 0x%x (wasted 0x%x)\n",
+                      size, jeb->used_size, jeb->wasted_size);
+               BUG();
+       }
+       if (jeb->last_node && ref_obsolete(jeb->last_node)) {
+#ifdef TEST_TOTLEN
+               jeb->last_node->__totlen += size;
+#endif
+               c->dirty_size += size;
+               c->free_size -= size;
+               jeb->dirty_size += size;
+               jeb->free_size -= size;
+       } else {
+               uint32_t ofs = jeb->offset + c->sector_size - jeb->free_size;
+               ofs |= REF_OBSOLETE;
+
+               jffs2_link_node_ref(c, jeb, ofs, size, NULL);
+       }
+
+       return 0;
+}
+
+/* Calculate totlen from surrounding nodes or eraseblock */
+static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
+                                   struct jffs2_eraseblock *jeb,
+                                   struct jffs2_raw_node_ref *ref)
+{
+       uint32_t ref_end;
+       struct jffs2_raw_node_ref *next_ref = ref_next(ref);
+
+       if (next_ref)
+               ref_end = ref_offset(next_ref);
+       else {
+               if (!jeb)
+                       jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+               /* Last node in block. Use free_space */
+               if (ref != jeb->last_node) {
+                       printk(KERN_CRIT "ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n",
+                              ref, ref_offset(ref), jeb->last_node, jeb->last_node?ref_offset(jeb->last_node):0);
+                       BUG();
+               }
+               ref_end = jeb->offset + c->sector_size - jeb->free_size;
+       }
+       return ref_end - ref_offset(ref);
+}
+
+uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+                           struct jffs2_raw_node_ref *ref)
+{
+       uint32_t ret;
+
+#if CONFIG_JFFS2_FS_DEBUG > 0
+       if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
+               printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
+                      jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
+               BUG();
+       }
+#endif
+
+       ret = __ref_totlen(c, jeb, ref);
+#ifdef TEST_TOTLEN
+       if (ret != ref->__totlen) {
+               printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
+                      ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
+                      ret, ref->__totlen);
+               if (ref_next(ref)) {
+                       printk(KERN_CRIT "next %p (0x%08x-0x%08x)\n", ref_next(ref), ref_offset(ref_next(ref)),
+                              ref_offset(ref_next(ref))+ref->__totlen);
+               } else 
+                       printk(KERN_CRIT "No next ref. jeb->last_node is %p\n", jeb->last_node);
+
+               printk(KERN_CRIT "jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n", jeb->wasted_size, jeb->dirty_size, jeb->used_size, jeb->free_size);
+               ret = ref->__totlen;
+               if (!jeb)
+                       jeb = &c->blocks[ref->flash_offset / c->sector_size];
+#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
+               __jffs2_dbg_dump_node_refs_nolock(c, jeb);
+#endif
+               WARN_ON(1);
+       }
+#endif /* TEST_TOTLEN */
+       return ret;
+}