]> err.no Git - linux-2.6/blobdiff - fs/jffs2/readinode.c
[JFFS2] Obsolete dirent nodes immediately on unlink, where possible.
[linux-2.6] / fs / jffs2 / readinode.c
index f1695642d0f7f218757fbdcff49ac0e0ed440a35..1298848336b8139e099eb51b757e34b610e9835d 100644 (file)
@@ -66,7 +66,7 @@ static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
                        jffs2_free_full_dnode(tn->fn);
                        jffs2_free_tmp_dnode_info(tn);
 
-                       this = this->rb_parent;
+                       this = rb_parent(this);
                        if (!this)
                                break;
 
@@ -116,19 +116,42 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r
                                uint32_t *latest_mctime, uint32_t *mctime_ver)
 {
        struct jffs2_full_dirent *fd;
+       uint32_t crc;
 
-       /* 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)))) {
-               JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
-                      ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
+       crc = crc32(0, rd, sizeof(*rd) - 8);
+       if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+               JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n",
+                            ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
                return 1;
        }
 
+       /* If we've never checked the CRCs on this node, check them now */
+       if (ref_flags(ref) == REF_UNCHECKED) {
+               struct jffs2_eraseblock *jeb;
+               int len;
+
+               /* Sanity check */
+               if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
+                       JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
+                                   ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
+                       return 1;
+               }
+
+               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;
+               ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+               spin_unlock(&c->erase_completion_lock);
+       }
+
        fd = jffs2_alloc_full_dirent(rd->nsize + 1);
        if (unlikely(!fd))
                return -ENOMEM;
@@ -198,13 +221,21 @@ 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;
+       uint32_t crc;
 
        /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
        BUG_ON(ref_obsolete(ref));
 
+       crc = crc32(0, rd, sizeof(*rd) - 8);
+       if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
+               JFFS2_NOTICE("node CRC failed on dnode at %#08x: read %#08x, calculated %#08x\n",
+                            ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
+               return 1;
+       }
+
        tn = jffs2_alloc_tmp_dnode_info();
        if (!tn) {
-               JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn));
+               JFFS2_ERROR("failed to allocate tn (%zu bytes).\n", sizeof(*tn));
                return -ENOMEM;
        }
 
@@ -213,14 +244,6 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
 
        /* If we've never checked the CRCs on this node, check them now */
        if (ref_flags(ref) == REF_UNCHECKED) {
-               uint32_t crc;
-
-               crc = crc32(0, rd, sizeof(*rd) - 8);
-               if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
-                       JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
-                                       ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
-                       goto free_out;
-               }
 
                /* Sanity checks */
                if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
@@ -343,48 +366,48 @@ free_out:
  * Helper function for jffs2_get_inode_nodes().
  * It is called every time an unknown node is found.
  *
- * Returns: 0 on succes;
+ * 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)
 {
        /* We don't mark unknown nodes as REF_UNCHECKED */
-       BUG_ON(ref_flags(ref) == REF_UNCHECKED);
+       if (ref_flags(ref) == REF_UNCHECKED) {
+               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));
+               return 1;
+       }
 
        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. */
-               JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref));
-               jffs2_dbg_dump_node(c, ref_offset(ref));
-               return 1;
-       } else {
-               switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
+       switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
 
-               case JFFS2_FEATURE_INCOMPAT:
-                       JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n",
-                               je16_to_cpu(un->nodetype), ref_offset(ref));
-                       /* EEP */
-                       BUG();
-                       break;
+       case JFFS2_FEATURE_INCOMPAT:
+               JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n",
+                           je16_to_cpu(un->nodetype), ref_offset(ref));
+               /* EEP */
+               BUG();
+               break;
 
-               case JFFS2_FEATURE_ROCOMPAT:
-                       JFFS2_ERROR("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_ROCOMPAT:
+               JFFS2_ERROR("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:
-                       JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
-                                       je16_to_cpu(un->nodetype), ref_offset(ref));
-                       break;
+       case JFFS2_FEATURE_RWCOMPAT_COPY:
+               JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
+                            je16_to_cpu(un->nodetype), ref_offset(ref));
+               break;
 
-               case JFFS2_FEATURE_RWCOMPAT_DELETE:
-                       JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
-                                       je16_to_cpu(un->nodetype), ref_offset(ref));
-                       return 1;
-               }
+       case JFFS2_FEATURE_RWCOMPAT_DELETE:
+               JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
+                            je16_to_cpu(un->nodetype), ref_offset(ref));
+               return 1;
        }
 
        return 0;
@@ -398,49 +421,38 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
  *         negative error code on failure.
  */
 static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
-                    int right_size, int *rdlen, unsigned char *buf, unsigned char *bufstart)
+                    int needed_len, int *rdlen, unsigned char *buf)
 {
-       int right_len, err, len;
+       int err, to_read = needed_len - *rdlen;
        size_t retlen;
        uint32_t offs;
 
        if (jffs2_is_writebuffered(c)) {
-               right_len = c->wbuf_pagesize - (bufstart - buf);
-               if (right_size + (int)(bufstart - buf) > c->wbuf_pagesize)
-                       right_len += c->wbuf_pagesize;
-       } else
-               right_len = right_size;
+               int rem = to_read % c->wbuf_pagesize;
 
-       if (*rdlen == right_len)
-               return 0;
+               if (rem)
+                       to_read += c->wbuf_pagesize - rem;
+       }
 
        /* We need to read more data */
        offs = ref_offset(ref) + *rdlen;
-       if (jffs2_is_writebuffered(c)) {
-               bufstart = buf + c->wbuf_pagesize;
-               len = c->wbuf_pagesize;
-       } else {
-               bufstart = buf + *rdlen;
-               len = right_size - *rdlen;
-       }
 
-       dbg_readinode("read more %d bytes\n", len);
+       dbg_readinode("read more %d bytes\n", to_read);
 
-       err = jffs2_flash_read(c, offs, len, &retlen, bufstart);
+       err = jffs2_flash_read(c, offs, to_read, &retlen, buf + *rdlen);
        if (err) {
                JFFS2_ERROR("can not read %d bytes from 0x%08x, "
-                       "error code: %d.\n", len, offs, err);
+                       "error code: %d.\n", to_read, offs, err);
                return err;
        }
 
-       if (retlen < len) {
-               JFFS2_ERROR("short read at %#08x: %d instead of %d.\n",
-                               offs, retlen, len);
+       if (retlen < to_read) {
+               JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n",
+                               offs, retlen, to_read);
                return -EIO;
        }
 
-       *rdlen = right_len;
-
+       *rdlen += to_read;
        return 0;
 }
 
@@ -463,27 +475,9 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
 
        dbg_readinode("ino #%u\n", f->inocache->ino);
 
-       if (jffs2_is_writebuffered(c)) {
-               /*
-                * If we have the write buffer, we assume the minimal I/O unit
-                * is c->wbuf_pagesize. We implement some optimizations which in
-                * this case and we need a temporary buffer of size =
-                * 2*c->wbuf_pagesize bytes (see comments in read_dnode()).
-                * Basically, we want to read not only the node header, but the
-                * whole wbuf (NAND page in case of NAND) or 2, if the node
-                * header overlaps the border between the 2 wbufs.
-                */
-               len = 2*c->wbuf_pagesize;
-       } else {
-               /*
-                * When there is no write buffer, the size of the temporary
-                * buffer is the size of the larges node header.
-                */
-               len = sizeof(union jffs2_node_union);
-       }
-
        /* FIXME: in case of NOR and available ->point() this
         * needs to be fixed. */
+       len = sizeof(union jffs2_node_union) + c->wbuf_pagesize;
        buf = kmalloc(len, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
@@ -493,8 +487,6 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
        if (!valid_ref && f->inocache->ino != 1)
                JFFS2_WARNING("Eep. No valid nodes for ino #%u.\n", f->inocache->ino);
        while (valid_ref) {
-               unsigned char *bufstart;
-
                /* We can hold a pointer to a non-obsolete node without the spinlock,
                   but _obsolete_ nodes may disappear at any time, if the block
                   they're in gets erased. So if we mark 'ref' obsolete while we're
@@ -510,51 +502,69 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
                /*
                 * At this point we don't know the type of the node we're going
                 * to read, so we do not know the size of its header. In order
-                * to minimize the amount of flash IO we assume the node has
-                * size = JFFS2_MIN_NODE_HEADER.
+                * to minimize the amount of flash IO we assume the header is
+                * of size = JFFS2_MIN_NODE_HEADER.
                 */
+               len = JFFS2_MIN_NODE_HEADER;
                if (jffs2_is_writebuffered(c)) {
+                       int end, rem;
+
                        /*
-                        * We treat 'buf' as 2 adjacent wbufs. We want to
-                        * adjust bufstart such as it points to the
-                        * beginning of the node within this wbuf.
+                        * We are about to read JFFS2_MIN_NODE_HEADER bytes,
+                        * but this flash has some minimal I/O unit. It is
+                        * possible that we'll need to read more soon, so read
+                        * up to the next min. I/O unit, in order not to
+                        * re-read the same min. I/O unit twice.
                         */
-                       bufstart = buf + (ref_offset(ref) % c->wbuf_pagesize);
-                       /* We will read either one wbuf or 2 wbufs. */
-                       len = c->wbuf_pagesize - (bufstart - buf);
-                       if (JFFS2_MIN_NODE_HEADER + (int)(bufstart - buf) > c->wbuf_pagesize) {
-                               /* The header spans the border of the first wbuf */
-                               len += c->wbuf_pagesize;
-                       }
-               } else {
-                       bufstart = buf;
-                       len = JFFS2_MIN_NODE_HEADER;
+                       end = ref_offset(ref) + len;
+                       rem = end % c->wbuf_pagesize;
+                       if (rem)
+                               end += c->wbuf_pagesize - rem;
+                       len = end - ref_offset(ref);
                }
 
                dbg_readinode("read %d bytes at %#08x(%d).\n", len, ref_offset(ref), ref_flags(ref));
 
                /* FIXME: point() */
-               err = jffs2_flash_read(c, ref_offset(ref), len,
-                                      &retlen, bufstart);
+               err = jffs2_flash_read(c, ref_offset(ref), len, &retlen, buf);
                if (err) {
                        JFFS2_ERROR("can not read %d bytes from 0x%08x, " "error code: %d.\n", len, ref_offset(ref), err);
                        goto free_out;
                }
 
                if (retlen < len) {
-                       JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ref_offset(ref), retlen, len);
+                       JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n", ref_offset(ref), retlen, len);
                        err = -EIO;
                        goto free_out;
                }
 
-               node = (union jffs2_node_union *)bufstart;
+               node = (union jffs2_node_union *)buf;
+
+               /* No need to mask in the valid bit; it shouldn't be invalid */
+               if (je32_to_cpu(node->u.hdr_crc) != crc32(0, node, sizeof(node->u)-4)) {
+                       JFFS2_NOTICE("Node header CRC failed at %#08x. {%04x,%04x,%08x,%08x}\n",
+                                    ref_offset(ref), 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_dbg_dump_node(c, ref_offset(ref));
+                       jffs2_mark_node_obsolete(c, ref);
+                       goto cont;
+               }
+               /* Due to poor choice of crc32 seed, an all-zero node will have a correct CRC */
+               if (!je32_to_cpu(node->u.hdr_crc) && !je16_to_cpu(node->u.nodetype) &&
+                   !je16_to_cpu(node->u.magic) && !je32_to_cpu(node->u.totlen)) {
+                       JFFS2_NOTICE("All zero node header at %#08x.\n", ref_offset(ref));
+                       jffs2_mark_node_obsolete(c, ref);
+                       goto cont;
+               }
 
                switch (je16_to_cpu(node->u.nodetype)) {
 
                case JFFS2_NODETYPE_DIRENT:
 
                        if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_raw_dirent)) {
-                               err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf, bufstart);
+                               err = read_more(c, ref, sizeof(struct jffs2_raw_dirent), &len, buf);
                                if (unlikely(err))
                                        goto free_out;
                        }
@@ -574,7 +584,7 @@ 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)) {
-                               err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf, bufstart);
+                               err = read_more(c, ref, sizeof(struct jffs2_raw_inode), &len, buf);
                                if (unlikely(err))
                                        goto free_out;
                        }
@@ -593,7 +603,7 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
 
                default:
                        if (JFFS2_MIN_NODE_HEADER < sizeof(struct jffs2_unknown_node)) {
-                               err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf, bufstart);
+                               err = read_more(c, ref, sizeof(struct jffs2_unknown_node), &len, buf);
                                if (unlikely(err))
                                        goto free_out;
                        }
@@ -606,6 +616,7 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
                                goto free_out;
 
                }
+       cont:
                spin_lock(&c->erase_completion_lock);
        }
 
@@ -679,12 +690,12 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                        jffs2_mark_node_obsolete(c, fn->raw);
 
                BUG_ON(rb->rb_left);
-               if (rb->rb_parent && rb->rb_parent->rb_left == rb) {
+               if (rb_parent(rb) && rb_parent(rb)->rb_left == rb) {
                        /* We were then left-hand child of our parent. We need
                         * to move our own right-hand child into our place. */
                        repl_rb = rb->rb_right;
                        if (repl_rb)
-                               repl_rb->rb_parent = rb->rb_parent;
+                               rb_set_parent(repl_rb, rb_parent(rb));
                } else
                        repl_rb = NULL;
 
@@ -692,14 +703,14 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
 
                /* Remove the spent tn from the tree; don't bother rebalancing
                 * but put our right-hand child in our own place. */
-               if (tn->rb.rb_parent) {
-                       if (tn->rb.rb_parent->rb_left == &tn->rb)
-                               tn->rb.rb_parent->rb_left = repl_rb;
-                       else if (tn->rb.rb_parent->rb_right == &tn->rb)
-                               tn->rb.rb_parent->rb_right = repl_rb;
+               if (rb_parent(&tn->rb)) {
+                       if (rb_parent(&tn->rb)->rb_left == &tn->rb)
+                               rb_parent(&tn->rb)->rb_left = repl_rb;
+                       else if (rb_parent(&tn->rb)->rb_right == &tn->rb)
+                               rb_parent(&tn->rb)->rb_right = repl_rb;
                        else BUG();
                } else if (tn->rb.rb_right)
-                       tn->rb.rb_right->rb_parent = NULL;
+                       rb_set_parent(tn->rb.rb_right, NULL);
 
                jffs2_free_tmp_dnode_info(tn);
                if (ret) {
@@ -915,13 +926,12 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
 {
        struct jffs2_raw_inode n;
-       struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
+       struct jffs2_inode_info *f = kzalloc(sizeof(*f), GFP_KERNEL);
        int ret;
 
        if (!f)
                return -ENOMEM;
 
-       memset(f, 0, sizeof(*f));
        init_MUTEX_LOCKED(&f->sem);
        f->inocache = ic;
 
@@ -939,6 +949,8 @@ 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;