⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 readinode.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. * * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/crc32.h>#include <linux/pagemap.h>#include <linux/mtd/mtd.h>#include <linux/compiler.h>#include "nodelist.h"/* * Check the data CRC of the node. * * Returns: 0 if the data CRC is correct; * 	    1 - if incorrect; *	    error code if an error occured. */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);	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;	}	ofs += len;	len = tn->csize - len;	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. */	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: %zu instead of %u.\n", retlen, tn->csize);			c->mtd->unpoint(c->mtd, buffer, ofs, retlen);		} else if (err)			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		c->mtd->unpoint(c->mtd, buffer, 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		c->mtd->unpoint(c->mtd, buffer, ofs, len);#endif	return err;}/* * Helper function for jffs2_add_older_frag_to_fragtree(). * * Checks the node if we are in the checking stage. */static int check_tn_node(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn){	int ret;	BUG_ON(ref_obsolete(tn->fn->raw));	/* We only check the data CRC of unchecked nodes */	if (ref_flags(tn->fn->raw) != REF_UNCHECKED)		return 0;	dbg_readinode("check node %#04x-%#04x, phys offs %#08x\n",		      tn->fn->ofs, tn->fn->ofs + tn->fn->size, ref_offset(tn->fn->raw));	ret = check_node_data(c, tn);	if (unlikely(ret < 0)) {		JFFS2_ERROR("check_node_data() returned error: %d.\n",			ret);	} else if (unlikely(ret > 0)) {		dbg_readinode("CRC error, mark it obsolete.\n");		jffs2_mark_node_obsolete(c, tn->fn->raw);	}	return ret;}static struct jffs2_tmp_dnode_info *jffs2_lookup_tn(struct rb_root *tn_root, uint32_t offset){	struct rb_node *next;	struct jffs2_tmp_dnode_info *tn = NULL;	dbg_readinode("root %p, offset %d\n", tn_root, offset);	next = tn_root->rb_node;	while (next) {		tn = rb_entry(next, struct jffs2_tmp_dnode_info, rb);		if (tn->fn->ofs < offset)			next = tn->rb.rb_right;		else if (tn->fn->ofs >= offset)			next = tn->rb.rb_left;		else			break;	}	return tn;}static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *tn){	jffs2_mark_node_obsolete(c, tn->fn->raw);	jffs2_free_full_dnode(tn->fn);	jffs2_free_tmp_dnode_info(tn);}/* * This function is used when we read an inode. Data nodes arrive in * arbitrary order -- they may be older or newer than the nodes which * are already in the tree. Where overlaps occur, the older node can * be discarded as long as the newer passes the CRC check. We don't * bother to keep track of holes in this rbtree, and neither do we deal * with frags -- we can have multiple entries starting at the same * offset, and the one with the smallest length will come first in the * ordering. * * 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 *this;	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.	   Note that such nodes won't be REF_UNCHECKED since there are no data to	   check anyway. */	if (!tn->fn->size) {		if (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);		return 0;	}	/* Find the earliest node which _may_ be relevant to this one */	this = jffs2_lookup_tn(&rii->tn_root, tn->fn->ofs);	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)			this = tn_prev(this);		/* First node should never be marked overlapped */		BUG_ON(!this);		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;		dbg_readinode("Ponder this ver %d, 0x%x-0x%x\n",			      this->version, this->fn->ofs, this->fn->size);		if (this->version == tn->version) {			/* Version number collision means REF_PRISTINE GC. Accept either of them			   as long as the CRC is correct. Check the one we have already...  */			if (!check_tn_node(c, this)) {				/* The one we already had was OK. Keep it and throw away the new one */				dbg_readinode("Like old node. Throw away new\n");				jffs2_kill_tn(c, tn);				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);				jffs2_kill_tn(c, this);				/* Same overlapping from in front and behind */				return 0;			}		}		if (this->version < tn->version &&		    this->fn->ofs >= tn->fn->ofs &&		    this->fn->ofs + this->fn->size <= fn_end) {			/* New node entirely overlaps 'this' */			if (check_tn_node(c, tn)) {				dbg_readinode("new node bad CRC\n");				jffs2_kill_tn(c, tn);				return 0;			}			/* ... 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);					dbg_readinode("Kill overlapped ver %d, 0x%x-0x%x\n",						      this->version, this->fn->ofs,						      this->fn->ofs+this->fn->size);					jffs2_kill_tn(c, this);				}				this = next;			}			dbg_readinode("Done killing overlapped nodes\n");			continue;		}		if (this->version > tn->version &&		    this->fn->ofs <= tn->fn->ofs &&		    this->fn->ofs+this->fn->size >= fn_end) {			/* New node entirely overlapped by 'this' */			if (!check_tn_node(c, this)) {				dbg_readinode("Good CRC on old node. Kill new\n");				jffs2_kill_tn(c, tn);				return 0;			}			/* ... but 'this' was bad. Replace it... */			dbg_readinode("Bad CRC on old overlapping node. Kill it\n");			tn_erase(this, &rii->tn_root);			jffs2_kill_tn(c, this);			break;		}		this = tn_next(this);	}	/* We neither completely obsoleted nor were completely	   obsoleted by an earlier node. Insert into the tree */	{		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;			insert_point = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);			if (tn->fn->ofs > insert_point->fn->ofs)				link = &insert_point->rb.rb_right;			else if (tn->fn->ofs < insert_point->fn->ofs ||				 tn->fn->size < insert_point->fn->size)				link = &insert_point->rb.rb_left;			else				link = &insert_point->rb.rb_right;		}		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) {		while (1) {			if (this->fn->ofs + this->fn->size > tn->fn->ofs) {				dbg_readinode("Node is overlapped by %p (v %d, 0x%x-0x%x)\n",					      this, this->version, this->fn->ofs,					      this->fn->ofs+this->fn->size);				tn->overlapped = 1;				break;			}			if (!this->overlapped)				break;			this = tn_prev(this);		}	}	/* If the new node overlaps anything ahead, note it */	this = tn_next(tn);	while (this && this->fn->ofs < fn_end) {		this->overlapped = 1;		dbg_readinode("Node ver %d, 0x%x-0x%x is overlapped\n",			      this->version, this->fn->ofs,			      this->fn->ofs+this->fn->size);		this = tn_next(this);	}	return 0;}/* Trivial function to remove the last node in the tree. Which by definition   has no right-hand -- so can be removed just by making its only child (if   any) take its place under its parent. */static void eat_last(struct rb_root *root, struct rb_node *node){	struct rb_node *parent = rb_parent(node);	struct rb_node **link;	/* LAST! */	BUG_ON(node->rb_right);	if (!parent)		link = &root->rb_node;	else if (node == parent->rb_left)		link = &parent->rb_left;	else		link = &parent->rb_right;	*link = node->rb_left;	/* Colour doesn't matter now. Only the parent pointer. */	if (node->rb_left)		node->rb_left->rb_parent_color = node->rb_parent_color;}/* We put this in reverse order, so we can just use eat_last */static void ver_insert(struct rb_root *ver_root, struct jffs2_tmp_dnode_info *tn){	struct rb_node **link = &ver_root->rb_node;	struct rb_node *parent = NULL;	struct jffs2_tmp_dnode_info *this_tn;	while (*link) {		parent = *link;		this_tn = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);		if (tn->version > this_tn->version)			link = &parent->rb_left;		else			link = &parent->rb_right;	}	dbg_readinode("Link new node at %p (root is %p)\n", link, ver_root);	rb_link_node(&tn->rb, parent, link);	rb_insert_color(&tn->rb, ver_root);}/* Build final, normal fragtree from tn tree. It doesn't matter which order   we add nodes to the real fragtree, as long as they don't overlap. And   having thrown away the majority of overlapped nodes as we went, there   really shouldn't be many sets of nodes which do overlap. If we start at   the end, we can use the overlap markers -- we can just eat nodes which   aren't overlapped, and when we encounter nodes which _do_ overlap we   sort them all into a temporary tree in version order before replaying them. */static int jffs2_build_inode_fragtree(struct jffs2_sb_info *c,				      struct jffs2_inode_info *f,				      struct jffs2_readinode_info *rii){	struct jffs2_tmp_dnode_info *pen, *last, *this;	struct rb_root ver_root = RB_ROOT;	uint32_t high_ver = 0;	if (rii->mdata_tn) {		dbg_readinode("potential mdata is ver %d at %p\n", rii->mdata_tn->version, rii->mdata_tn);		high_ver = rii->mdata_tn->version;		rii->latest_ref = rii->mdata_tn->fn->raw;	}#ifdef JFFS2_DBG_READINODE_MESSAGES	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 = tn_prev(this);	}#endif	pen = tn_last(&rii->tn_root);	while ((last = pen)) {		pen = tn_prev(last);		eat_last(&rii->tn_root, &last->rb);		ver_insert(&ver_root, last);		if (unlikely(last->overlapped))			continue;		/* Now we have a bunch of nodes in reverse version		   order, in the tree at ver_root. Most of the time,		   there'll actually be only one node in the 'tree',		   in fact. */		this = tn_last(&ver_root);		while (this) {			struct jffs2_tmp_dnode_info *vers_next;			int ret;			vers_next = tn_prev(this);			eat_last(&ver_root, &this->rb);			if (check_tn_node(c, this)) {				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);			} else {				if (this->version > high_ver) {					/* Note that this is different from the other

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -