in fs/yaffs2/yaffs_yaffs2.c [934:1338]
static inline int yaffs2_scan_chunk(struct yaffs_dev *dev,
struct yaffs_block_info *bi,
int blk, int chunk_in_block,
int *found_chunks,
u8 *chunk_data,
struct list_head *hard_list,
int summary_available)
{
struct yaffs_obj_hdr *oh;
struct yaffs_obj *in;
struct yaffs_obj *parent;
int equiv_id;
loff_t file_size;
int is_shrink;
int is_unlinked;
struct yaffs_ext_tags tags;
int alloc_failed = 0;
int chunk = blk * dev->param.chunks_per_block + chunk_in_block;
struct yaffs_file_var *file_var;
struct yaffs_hardlink_var *hl_var;
struct yaffs_symlink_var *sl_var;
if (summary_available) {
yaffs_summary_fetch(dev, &tags, chunk_in_block);
tags.seq_number = bi->seq_number;
}
if (!summary_available || tags.obj_id == 0) {
yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags);
dev->tags_used++;
} else {
dev->summary_used++;
}
/* Let's have a good look at this chunk... */
if (!tags.chunk_used) {
/* An unassigned chunk in the block.
* If there are used chunks after this one, then
* it is a chunk that was skipped due to failing
* the erased check. Just skip it so that it can
* be deleted.
* But, more typically, We get here when this is
* an unallocated chunk and his means that
* either the block is empty or this is the one
* being allocated from
*/
if (*found_chunks) {
/* This is a chunk that was skipped due
* to failing the erased check */
} else if (chunk_in_block == 0) {
/* We're looking at the first chunk in
* the block so the block is unused */
bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
dev->n_erased_blocks++;
} else {
if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
if (dev->seq_number == bi->seq_number) {
/* Allocating from this block*/
yaffs_trace(YAFFS_TRACE_SCAN,
" Allocating from %d %d",
blk, chunk_in_block);
bi->block_state =
YAFFS_BLOCK_STATE_ALLOCATING;
dev->alloc_block = blk;
dev->alloc_page = chunk_in_block;
dev->alloc_block_finder = blk;
} else {
/* This is a partially written block
* that is not the current
* allocation block.
*/
yaffs_trace(YAFFS_TRACE_SCAN,
"Partially written block %d detected. gc will fix this.",
blk);
}
}
}
dev->n_free_chunks++;
} else if (tags.ecc_result ==
YAFFS_ECC_RESULT_UNFIXED) {
yaffs_trace(YAFFS_TRACE_SCAN,
" Unfixed ECC in chunk(%d:%d), chunk ignored",
blk, chunk_in_block);
dev->n_free_chunks++;
} else if (tags.obj_id > YAFFS_MAX_OBJECT_ID ||
tags.chunk_id > YAFFS_MAX_CHUNK_ID ||
tags.obj_id == YAFFS_OBJECTID_SUMMARY ||
(tags.chunk_id > 0 &&
tags.n_bytes > dev->data_bytes_per_chunk) ||
tags.seq_number != bi->seq_number) {
yaffs_trace(YAFFS_TRACE_SCAN,
"Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored",
blk, chunk_in_block, tags.obj_id,
tags.chunk_id, tags.n_bytes);
dev->n_free_chunks++;
} else if (tags.chunk_id > 0) {
/* chunk_id > 0 so it is a data chunk... */
loff_t endpos;
loff_t chunk_base = (tags.chunk_id - 1) *
dev->data_bytes_per_chunk;
*found_chunks = 1;
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
bi->pages_in_use++;
in = yaffs_find_or_create_by_number(dev,
tags.obj_id,
YAFFS_OBJECT_TYPE_FILE);
if (!in)
/* Out of memory */
alloc_failed = 1;
if (in &&
in->variant_type == YAFFS_OBJECT_TYPE_FILE &&
chunk_base < in->variant.file_variant.shrink_size) {
/* This has not been invalidated by
* a resize */
if (!yaffs_put_chunk_in_file(in, tags.chunk_id,
chunk, -1))
alloc_failed = 1;
/* File size is calculated by looking at
* the data chunks if we have not
* seen an object header yet.
* Stop this practice once we find an
* object header.
*/
endpos = chunk_base + tags.n_bytes;
if (!in->valid &&
in->variant.file_variant.scanned_size < endpos) {
in->variant.file_variant.
scanned_size = endpos;
in->variant.file_variant.
file_size = endpos;
}
} else if (in) {
/* This chunk has been invalidated by a
* resize, or a past file deletion
* so delete the chunk*/
yaffs_chunk_del(dev, chunk, 1, __LINE__);
}
} else {
/* chunk_id == 0, so it is an ObjectHeader.
* Thus, we read in the object header and make
* the object
*/
*found_chunks = 1;
yaffs_set_chunk_bit(dev, blk, chunk_in_block);
bi->pages_in_use++;
oh = NULL;
in = NULL;
if (tags.extra_available) {
in = yaffs_find_or_create_by_number(dev,
tags.obj_id,
tags.extra_obj_type);
if (!in)
alloc_failed = 1;
}
if (!in ||
(!in->valid && dev->param.disable_lazy_load) ||
tags.extra_shadows ||
(!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT ||
tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) {
/* If we don't have valid info then we
* need to read the chunk
* TODO In future we can probably defer
* reading the chunk and living with
* invalid data until needed.
*/
yaffs_rd_chunk_tags_nand(dev, chunk, chunk_data, NULL);
oh = (struct yaffs_obj_hdr *)chunk_data;
if (dev->param.inband_tags) {
/* Fix up the header if they got
* corrupted by inband tags */
oh->shadows_obj =
oh->inband_shadowed_obj_id;
oh->is_shrink =
oh->inband_is_shrink;
}
if (!in) {
in = yaffs_find_or_create_by_number(dev,
tags.obj_id, oh->type);
if (!in)
alloc_failed = 1;
}
}
if (!in) {
/* TODO Hoosterman we have a problem! */
yaffs_trace(YAFFS_TRACE_ERROR,
"yaffs tragedy: Could not make object for object %d at chunk %d during scan",
tags.obj_id, chunk);
return YAFFS_FAIL;
}
if (in->valid) {
/* We have already filled this one.
* We have a duplicate that will be
* discarded, but we first have to suck
* out resize info if it is a file.
*/
if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) &&
((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
(tags.extra_available &&
tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
)) {
loff_t this_size = (oh) ?
yaffs_oh_to_size(oh) :
tags.extra_file_size;
u32 parent_obj_id = (oh) ?
oh->parent_obj_id :
tags.extra_parent_id;
is_shrink = (oh) ?
oh->is_shrink :
tags.extra_is_shrink;
/* If it is deleted (unlinked
* at start also means deleted)
* we treat the file size as
* being zeroed at this point.
*/
if (parent_obj_id == YAFFS_OBJECTID_DELETED ||
parent_obj_id == YAFFS_OBJECTID_UNLINKED) {
this_size = 0;
is_shrink = 1;
}
if (is_shrink &&
in->variant.file_variant.shrink_size >
this_size)
in->variant.file_variant.shrink_size =
this_size;
if (is_shrink)
bi->has_shrink_hdr = 1;
}
/* Use existing - destroy this one. */
yaffs_chunk_del(dev, chunk, 1, __LINE__);
}
if (!in->valid && in->variant_type !=
(oh ? oh->type : tags.extra_obj_type))
yaffs_trace(YAFFS_TRACE_ERROR,
"yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan",
oh ? oh->type : tags.extra_obj_type,
in->variant_type, tags.obj_id,
chunk);
if (!in->valid &&
(tags.obj_id == YAFFS_OBJECTID_ROOT ||
tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) {
/* We only load some info, don't fiddle
* with directory structure */
in->valid = 1;
if (oh) {
in->yst_mode = oh->yst_mode;
yaffs_load_attribs(in, oh);
in->lazy_loaded = 0;
} else {
in->lazy_loaded = 1;
}
in->hdr_chunk = chunk;
} else if (!in->valid) {
/* we need to load this info */
in->valid = 1;
in->hdr_chunk = chunk;
if (oh) {
in->variant_type = oh->type;
in->yst_mode = oh->yst_mode;
yaffs_load_attribs(in, oh);
if (oh->shadows_obj > 0)
yaffs_handle_shadowed_obj(dev,
oh->shadows_obj, 1);
yaffs_set_obj_name_from_oh(in, oh);
parent = yaffs_find_or_create_by_number(dev,
oh->parent_obj_id,
YAFFS_OBJECT_TYPE_DIRECTORY);
file_size = yaffs_oh_to_size(oh);
is_shrink = oh->is_shrink;
equiv_id = oh->equiv_id;
} else {
in->variant_type = tags.extra_obj_type;
parent = yaffs_find_or_create_by_number(dev,
tags.extra_parent_id,
YAFFS_OBJECT_TYPE_DIRECTORY);
file_size = tags.extra_file_size;
is_shrink = tags.extra_is_shrink;
equiv_id = tags.extra_equiv_id;
in->lazy_loaded = 1;
}
in->dirty = 0;
if (!parent)
alloc_failed = 1;
/* directory stuff...
* hook up to parent
*/
if (parent &&
parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) {
/* Set up as a directory */
parent->variant_type =
YAFFS_OBJECT_TYPE_DIRECTORY;
INIT_LIST_HEAD(&parent->
variant.dir_variant.children);
} else if (!parent ||
parent->variant_type !=
YAFFS_OBJECT_TYPE_DIRECTORY) {
/* Hoosterman, another problem....
* Trying to use a non-directory as a directory
*/
yaffs_trace(YAFFS_TRACE_ERROR,
"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
);
parent = dev->lost_n_found;
}
yaffs_add_obj_to_dir(parent, in);
is_unlinked = (parent == dev->del_dir) ||
(parent == dev->unlinked_dir);
if (is_shrink)
/* Mark the block */
bi->has_shrink_hdr = 1;
/* Note re hardlinks.
* Since we might scan a hardlink before its equivalent
* object is scanned we put them all in a list.
* After scanning is complete, we should have all the
* objects, so we run through this list and fix up all
* the chains.
*/
switch (in->variant_type) {
case YAFFS_OBJECT_TYPE_UNKNOWN:
/* Todo got a problem */
break;
case YAFFS_OBJECT_TYPE_FILE:
file_var = &in->variant.file_variant;
if (file_var->scanned_size < file_size) {
/* This covers the case where the file
* size is greater than the data held.
* This will happen if the file is
* resized to be larger than its
* current data extents.
*/
file_var->file_size = file_size;
file_var->scanned_size = file_size;
}
if (file_var->shrink_size > file_size)
file_var->shrink_size = file_size;
break;
case YAFFS_OBJECT_TYPE_HARDLINK:
hl_var = &in->variant.hardlink_variant;
if (!is_unlinked) {
hl_var->equiv_id = equiv_id;
list_add(&in->hard_links, hard_list);
}
break;
case YAFFS_OBJECT_TYPE_DIRECTORY:
/* Do nothing */
break;
case YAFFS_OBJECT_TYPE_SPECIAL:
/* Do nothing */
break;
case YAFFS_OBJECT_TYPE_SYMLINK:
sl_var = &in->variant.symlink_variant;
if (oh) {
sl_var->alias =
yaffs_clone_str(oh->alias);
if (!sl_var->alias)
alloc_failed = 1;
}
break;
}
}
}
return alloc_failed ? YAFFS_FAIL : YAFFS_OK;
}