Example #1
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, struct rb_root *tnp, int rdlen,
			     uint32_t *latest_mctime, uint32_t *mctime_ver)
{
	struct jffs2_tmp_dnode_info *tn;
	uint32_t len, csize;
	int ret = 1;

	/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
	BUG_ON(ref_obsolete(ref));

	tn = jffs2_alloc_tmp_dnode_info();
	if (!tn) {
		JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn));
		return -ENOMEM;
	}

	tn->partial_crc = 0;
	csize = je32_to_cpu(rd->csize);

	/* 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)) ||
		    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));
			goto free_out;
		}

		if (jffs2_is_writebuffered(c) && csize != 0) {
			/* At this point we are supposed to check the data CRC
			 * of our unchecked node. But thus far, we do not
			 * know whether the node is valid or obsolete. To
			 * figure this out, we need to walk all the nodes of
			 * the inode and build the inode fragtree. We don't
			 * want to spend time checking data of nodes which may
			 * later be found to be obsolete. So we put off the full
			 * data CRC checking until we have read all the inode
			 * nodes and have started building the fragtree.
			 *
			 * The fragtree is being built starting with nodes
			 * having the highest version number, so we'll be able
			 * to detect whether a node is valid (i.e., it is not
			 * overlapped by a node with higher version) or not.
			 * And we'll be able to check only those nodes, which
			 * are not obsolete.
			 *
			 * Of course, this optimization only makes sense in case
			 * of NAND flashes (or other flashes whith
			 * !jffs2_can_mark_obsolete()), since on NOR flashes
			 * nodes are marked obsolete physically.
			 *
			 * Since NAND flashes (or other flashes with
			 * jffs2_is_writebuffered(c)) are anyway read by
			 * fractions of c->wbuf_pagesize, and we have just read
			 * the node header, it is likely that the starting part
			 * of the node data is also read when we read the
			 * header. So we don't mind to check the CRC of the
			 * starting part of the data of the node now, and check
			 * the second part later (in jffs2_check_node_data()).
			 * Of course, we will not need to re-read and re-check
			 * the NAND page which we have just read. This is why we
			 * read the whole NAND page at jffs2_get_inode_nodes(),
			 * while we needed only the node header.
			 */
			unsigned char *buf;

			/* 'buf' will point to the start of data */
			buf = (unsigned char *)rd + sizeof(*rd);
			/* len will be the read data length */
			len = min_t(uint32_t, rdlen - sizeof(*rd), csize);
			tn->partial_crc = crc32(0, buf, len);

			dbg_readinode("Calculates CRC (%#08x) for %d bytes, csize %d\n", tn->partial_crc, len, csize);

			/* If we actually calculated the whole data CRC
			 * and it is wrong, drop the node. */
			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));
				goto free_out;
			}

		} else if (csize == 0) {
			/*
			 * We checked the header CRC. If the node has no data, adjust
			 * the space accounting now. For other nodes this will be done
			 * later either when the node is marked obsolete or when its
			 * data is checked.
			 */
			struct jffs2_eraseblock *jeb;

			dbg_readinode("the node has no data.\n");
			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_NORMAL;
			spin_unlock(&c->erase_completion_lock);
		}
	}

	tn->fn = jffs2_alloc_full_dnode();
	if (!tn->fn) {
		JFFS2_ERROR("alloc fn failed\n");
		ret = -ENOMEM;
		goto free_out;
	}

	tn->version = je32_to_cpu(rd->version);
	tn->fn->ofs = je32_to_cpu(rd->offset);
	tn->data_crc = je32_to_cpu(rd->data_crc);
	tn->csize = csize;
	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) && csize)
		tn->fn->size = csize;
	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);

	jffs2_add_tn_to_tree(tn, tnp);

	return 0;

free_out:
	jffs2_free_tmp_dnode_info(tn);
	return ret;
}
Example #2
0
static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
				 struct rb_root *tnp, struct jffs2_full_dirent **fdp,
				 uint32_t *highest_version, uint32_t *latest_mctime,
				 uint32_t *mctime_ver)
{
	struct jffs2_raw_node_ref *ref, *valid_ref;
	struct rb_root ret_tn = RB_ROOT;
	struct jffs2_full_dirent *ret_fd = NULL;
	union jffs2_node_union node;
	size_t retlen;
	int err;

	*mctime_ver = 0;
	
	JFFS2_DBG_READINODE("ino #%u\n", f->inocache->ino);

	spin_lock(&c->erase_completion_lock);

	valid_ref = jffs2_first_valid_node(f->inocache->nodes);

	if (!valid_ref && (f->inocache->ino != 1))
		JFFS2_WARNING("no valid nodes for ino #%u\n", f->inocache->ino);

	while (valid_ref) {
		/* 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
		   not holding the lock, it can go away immediately. For that reason,
		   we find the next valid node first, before processing 'ref'.
		*/
		ref = valid_ref;
		valid_ref = jffs2_first_valid_node(ref->next_in_ino);
		spin_unlock(&c->erase_completion_lock);

		cond_resched();

		/* FIXME: point() */
		err = jffs2_flash_read(c, (ref_offset(ref)), 
				       min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
				       &retlen, (void *)&node);
		if (err) {
			JFFS2_ERROR("error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
			goto free_out;
		}
			
		switch (je16_to_cpu(node.u.nodetype)) {
			
		case JFFS2_NODETYPE_DIRENT:
			JFFS2_DBG_READINODE("node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref));
			
			if (retlen < sizeof(node.d)) {
				JFFS2_ERROR("short read dirent at %#08x\n", ref_offset(ref));
				err = -EIO;
				goto free_out;
			}

			err = read_direntry(c, ref, &node.d, retlen, &ret_fd, (int32_t *)latest_mctime, mctime_ver);
			if (err == 1) {
				jffs2_mark_node_obsolete(c, ref);
				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);

			break;

		case JFFS2_NODETYPE_INODE:
			JFFS2_DBG_READINODE("node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref));
			
			if (retlen < sizeof(node.i)) {
				JFFS2_ERROR("short read dnode at %#08x\n", ref_offset(ref));
				err = -EIO;
				goto free_out;
			}

			err = read_dnode(c, ref, &node.i, retlen, &ret_tn, (int32_t *)latest_mctime, mctime_ver);
			if (err == 1) {
				jffs2_mark_node_obsolete(c, ref);
				break;
			} else if (unlikely(err))
				goto free_out;

			if (je32_to_cpu(node.i.version) > *highest_version)
				*highest_version = je32_to_cpu(node.i.version);
			
			JFFS2_DBG_READINODE("version %d, highest_version now %d\n",
					je32_to_cpu(node.i.version), *highest_version);

			break;

		default:
			/* Check we've managed to read at least the common node header */
			if (retlen < sizeof(struct jffs2_unknown_node)) {
				JFFS2_ERROR("short read unknown node at %#08x\n", ref_offset(ref));
				return -EIO;
			}

			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);

	}
	spin_unlock(&c->erase_completion_lock);
	*tnp = ret_tn;
	*fdp = ret_fd;

	return 0;

 free_out:
	jffs2_free_tmp_dnode_info_list(&ret_tn);
	jffs2_free_full_dirent_list(ret_fd);
	return err;
}
Example #3
0
/*
 * Helper function for jffs2_get_inode_nodes().
 * It is called every time an directory entry node is found.
 *
 * 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,
				struct jffs2_raw_dirent *rd, size_t read,
				struct jffs2_readinode_info *rii)
{
	struct jffs2_full_dirent *fd;
	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("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n",
			     ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
		jffs2_mark_node_obsolete(c, ref);
		return 0;
	}

	/* 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));
			jffs2_mark_node_obsolete(c, ref);
			return 0;
		}

		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) | dirent_node_state(rd);
		spin_unlock(&c->erase_completion_lock);
	}

	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;

	if (fd->version > rii->highest_version)
		rii->highest_version = fd->version;

	/* Pick out the mctime of the latest dirent */
	if(fd->version > rii->mctime_ver && je32_to_cpu(rd->mctime)) {
		rii->mctime_ver = fd->version;
		rii->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)) {
			JFFS2_ERROR("read remainder of name: 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
	 */
	jffs2_add_fd_to_list(c, fd, &rii->fds);

	return 0;
}
Example #4
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))) {
			JFFS2_NOTICE("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)))) {
				JFFS2_WARNING("inode node header CRC is corrupted at %#08x\n", ref_offset(ref));
				jffs2_dbg_dump_node(c, ref_offset(ref));
			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)) {
					JFFS2_ERROR("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)){
					JFFS2_ERROR("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)) {
				JFFS2_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)))) {
			JFFS2_DBG_READINODE("marking node at %#08x REF_PRISTINE\n", ref_offset(ref));
			ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
		} else {
			JFFS2_DBG_READINODE("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) {
		JFFS2_ERROR("alloc tn failed\n");
		return -ENOMEM;
	}

	tn->fn = jffs2_alloc_full_dnode();
	if (!tn->fn) {
		JFFS2_ERROR("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);

	JFFS2_DBG_READINODE("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;
}
Example #5
0
/*
 * Check the data CRC of the node.
 *
 * Returns: 0 if the data CRC is correct;
 * 	    1 - if incorrect;
 *	    error code if an error occurred.
 */
static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn)
{
	struct jffs2_raw_node_ref *ref = tn->fn->raw;
	int err = 0, pointed = 0;
	struct jffs2_eraseblock *jeb;
	unsigned char *buffer;
	uint32_t crc, ofs, len;
	size_t retlen;

	BUG_ON(tn->csize == 0);

	/* Calculate how many bytes were already checked */
	ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode);
	len = tn->csize;

	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);

#ifndef __ECOS
	/* TODO: instead, incapsulate point() stuff to jffs2_flash_read(),
	 * adding and jffs2_flash_read_end() interface. */
	err = 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);
		mtd_unpoint(c->mtd, ofs, retlen);
	} else if (err) {
		if (err != -EOPNOTSUPP)
			JFFS2_WARNING("MTD point failed: error code %d.\n", err);
	} else
		pointed = 1; /* succefully pointed to device */
#endif

	if (!pointed) {
		buffer = kmalloc(len, GFP_KERNEL);
		if (unlikely(!buffer))
			return -ENOMEM;

		/* TODO: this is very frequent pattern, make it a separate
		 * routine */
		err = jffs2_flash_read(c, ofs, len, &retlen, buffer);
		if (err) {
			JFFS2_ERROR("can not read %d bytes from 0x%08x, error code: %d.\n", len, ofs, err);
			goto free_out;
		}

		if (retlen != len) {
			JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len);
			err = -EIO;
			goto free_out;
		}
	}

	/* Continue calculating CRC */
	crc = crc32(tn->partial_crc, buffer, len);
	if(!pointed)
		kfree(buffer);
#ifndef __ECOS
	else
		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",
			     ref_offset(ref), tn->data_crc, crc);
		return 1;
	}

adj_acc:
	jeb = &c->blocks[ref->flash_offset / c->sector_size];
	len = ref_totlen(c, jeb, ref);
	/* If it should be REF_NORMAL, it'll get marked as such when
	   we build the fragtree, shortly. No need to worry about GC
	   moving it while it's marked REF_PRISTINE -- GC won't happen
	   till we've finished checking every inode anyway. */
	ref->flash_offset |= REF_PRISTINE;
	/*
	 * Mark the node as having been checked and fix the
	 * accounting accordingly.
	 */
	spin_lock(&c->erase_completion_lock);
	jeb->used_size += len;
	jeb->unchecked_size -= len;
	c->used_size += len;
	c->unchecked_size -= len;
	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
	spin_unlock(&c->erase_completion_lock);

	return 0;

free_out:
	if(!pointed)
		kfree(buffer);
#ifndef __ECOS
	else
		mtd_unpoint(c->mtd, ofs, len);
#endif
	return err;
}