static inline int yaffs2_scan_chunk()

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