📄 readinode.c
字号:
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: readinode.c,v 1.132 2005/07/28 14:46:40 dedekind Exp $
*
*/
#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"
void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
{
struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
JFFS2_DBG_FRAGTREE("truncating fragtree to 0x%08x bytes\n", size);
/* We know frag->ofs <= size. That's what lookup does for us */
if (frag && frag->ofs != size) {
if (frag->ofs+frag->size >= size) {
JFFS2_DBG_FRAGTREE2("truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size);
frag->size = size - frag->ofs;
}
frag = frag_next(frag);
}
while (frag && frag->ofs >= size) {
struct jffs2_node_frag *next = frag_next(frag);
JFFS2_DBG_FRAGTREE("removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size);
frag_erase(frag, list);
jffs2_obsolete_node_frag(c, frag);
frag = next;
}
}
/*
* Put a new tmp_dnode_info into the temporaty RB-tree, keeping the list in
* order of increasing version.
*/
static void jffs2_add_tn_to_tree(struct jffs2_tmp_dnode_info *tn, struct rb_root *list)
{
struct rb_node **p = &list->rb_node;
struct rb_node * parent = NULL;
struct jffs2_tmp_dnode_info *this;
while (*p) {
parent = *p;
this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
/* There may actually be a collision here, but it doesn't
actually matter. As long as the two nodes with the same
version are together, it's all fine. */
if (tn->version < this->version)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&tn->rb, parent, p);
rb_insert_color(&tn->rb, list);
}
static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
{
struct rb_node *this;
struct jffs2_tmp_dnode_info *tn;
this = list->rb_node;
/* Now at bottom of tree */
while (this) {
if (this->rb_left)
this = this->rb_left;
else if (this->rb_right)
this = this->rb_right;
else {
tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb);
jffs2_free_full_dnode(tn->fn);
jffs2_free_tmp_dnode_info(tn);
this = this->rb_parent;
if (!this)
break;
if (this->rb_left == &tn->rb)
this->rb_left = NULL;
else if (this->rb_right == &tn->rb)
this->rb_right = NULL;
else BUG();
}
}
list->rb_node = NULL;
}
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
{
struct jffs2_full_dirent *next;
while (fd) {
next = fd->next;
jffs2_free_full_dirent(fd);
fd = next;
}
}
/* Returns first valid node after 'ref'. May return 'ref' */
static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
{
while (ref && ref->next_in_ino) {
if (!ref_obsolete(ref))
return ref;
JFFS2_DBG_NODEREF("node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref));
ref = ref->next_in_ino;
}
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)))) {
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;
}
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)) {
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, 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))) {
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;
}
/*
* 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. */
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) {
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_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;
}
}
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 */
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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -