Home | History | Annotate | Download | only in yaffs2
      1 /*
      2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
      3  *
      4  * Copyright (C) 2002-2011 Aleph One Ltd.
      5  *   for Toby Churchill Ltd and Brightstar Engineering
      6  *
      7  * Created by Charles Manning <charles (at) aleph1.co.uk>
      8  *
      9  * This program is free software; you can redistribute it and/or modify
     10  * it under the terms of the GNU General Public License version 2 as
     11  * published by the Free Software Foundation.
     12  */
     13 
     14 #include "yaffs_yaffs1.h"
     15 #include "yportenv.h"
     16 #include "yaffs_trace.h"
     17 #include "yaffs_bitmap.h"
     18 #include "yaffs_getblockinfo.h"
     19 #include "yaffs_nand.h"
     20 #include "yaffs_attribs.h"
     21 
     22 int yaffs1_scan(struct yaffs_dev *dev)
     23 {
     24 	struct yaffs_ext_tags tags;
     25 	int blk;
     26 	int chunk;
     27 	int c;
     28 	int deleted;
     29 	enum yaffs_block_state state;
     30 	LIST_HEAD(hard_list);
     31 	struct yaffs_block_info *bi;
     32 	u32 seq_number;
     33 	struct yaffs_obj_hdr *oh;
     34 	struct yaffs_obj *in;
     35 	struct yaffs_obj *parent;
     36 	int alloc_failed = 0;
     37 	struct yaffs_shadow_fixer *shadow_fixers = NULL;
     38 	u8 *chunk_data;
     39 
     40 	yaffs_trace(YAFFS_TRACE_SCAN,
     41 		"yaffs1_scan starts  intstartblk %d intendblk %d...",
     42 		dev->internal_start_block, dev->internal_end_block);
     43 
     44 	chunk_data = yaffs_get_temp_buffer(dev);
     45 
     46 	dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
     47 
     48 	/* Scan all the blocks to determine their state */
     49 	bi = dev->block_info;
     50 	for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
     51 	     blk++) {
     52 		yaffs_clear_chunk_bits(dev, blk);
     53 		bi->pages_in_use = 0;
     54 		bi->soft_del_pages = 0;
     55 
     56 		yaffs_query_init_block_state(dev, blk, &state, &seq_number);
     57 
     58 		bi->block_state = state;
     59 		bi->seq_number = seq_number;
     60 
     61 		if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
     62 			bi->block_state = state = YAFFS_BLOCK_STATE_DEAD;
     63 
     64 		yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
     65 			"Block scanning block %d state %d seq %d",
     66 			blk, state, seq_number);
     67 
     68 		if (state == YAFFS_BLOCK_STATE_DEAD) {
     69 			yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
     70 				"block %d is bad", blk);
     71 		} else if (state == YAFFS_BLOCK_STATE_EMPTY) {
     72 			yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
     73 			dev->n_erased_blocks++;
     74 			dev->n_free_chunks += dev->param.chunks_per_block;
     75 		}
     76 		bi++;
     77 	}
     78 
     79 	/* For each block.... */
     80 	for (blk = dev->internal_start_block;
     81 	     !alloc_failed && blk <= dev->internal_end_block; blk++) {
     82 
     83 		cond_resched();
     84 
     85 		bi = yaffs_get_block_info(dev, blk);
     86 		state = bi->block_state;
     87 
     88 		deleted = 0;
     89 
     90 		/* For each chunk in each block that needs scanning.... */
     91 		for (c = 0;
     92 			!alloc_failed && c < dev->param.chunks_per_block &&
     93 			state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) {
     94 			/* Read the tags and decide what to do */
     95 			chunk = blk * dev->param.chunks_per_block + c;
     96 
     97 			yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags);
     98 
     99 			/* Let's have a good look at this chunk... */
    100 
    101 			if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED ||
    102 			    tags.is_deleted) {
    103 				/* YAFFS1 only...
    104 				 * A deleted chunk
    105 				 */
    106 				deleted++;
    107 				dev->n_free_chunks++;
    108 			} else if (!tags.chunk_used) {
    109 				/* An unassigned chunk in the block
    110 				 * This means that either the block is empty or
    111 				 * this is the one being allocated from
    112 				 */
    113 
    114 				if (c == 0) {
    115 					/* We're looking at the first chunk in
    116 					 *the block so the block is unused */
    117 					state = YAFFS_BLOCK_STATE_EMPTY;
    118 					dev->n_erased_blocks++;
    119 				} else {
    120 					/* this is the block being allocated */
    121 					yaffs_trace(YAFFS_TRACE_SCAN,
    122 						" Allocating from %d %d",
    123 						blk, c);
    124 					state = YAFFS_BLOCK_STATE_ALLOCATING;
    125 					dev->alloc_block = blk;
    126 					dev->alloc_page = c;
    127 					dev->alloc_block_finder = blk;
    128 
    129 				}
    130 
    131 				dev->n_free_chunks +=
    132 				    (dev->param.chunks_per_block - c);
    133 			} else if (tags.chunk_id > 0) {
    134 				/* chunk_id > 0 so it is a data chunk... */
    135 				unsigned int endpos;
    136 
    137 				yaffs_set_chunk_bit(dev, blk, c);
    138 				bi->pages_in_use++;
    139 
    140 				in = yaffs_find_or_create_by_number(dev,
    141 							tags.obj_id,
    142 							YAFFS_OBJECT_TYPE_FILE);
    143 				/* PutChunkIntoFile checks for a clash
    144 				 * (two data chunks with the same chunk_id).
    145 				 */
    146 
    147 				if (!in)
    148 					alloc_failed = 1;
    149 
    150 				if (in) {
    151 					if (!yaffs_put_chunk_in_file
    152 					    (in, tags.chunk_id, chunk, 1))
    153 						alloc_failed = 1;
    154 				}
    155 
    156 				endpos =
    157 				    (tags.chunk_id - 1) *
    158 				    dev->data_bytes_per_chunk +
    159 				    tags.n_bytes;
    160 				if (in &&
    161 				    in->variant_type ==
    162 				     YAFFS_OBJECT_TYPE_FILE &&
    163 				    in->variant.file_variant.scanned_size <
    164 				      endpos) {
    165 					in->variant.file_variant.scanned_size =
    166 					    endpos;
    167 					if (!dev->param.use_header_file_size) {
    168 						in->variant.
    169 						    file_variant.file_size =
    170 						    in->variant.
    171 						    file_variant.scanned_size;
    172 					}
    173 
    174 				}
    175 			} else {
    176 				/* chunk_id == 0, so it is an ObjectHeader.
    177 				 * Make the object
    178 				 */
    179 				yaffs_set_chunk_bit(dev, blk, c);
    180 				bi->pages_in_use++;
    181 
    182 				yaffs_rd_chunk_tags_nand(dev, chunk,
    183 							 chunk_data, NULL);
    184 
    185 				oh = (struct yaffs_obj_hdr *)chunk_data;
    186 
    187 				in = yaffs_find_by_number(dev, tags.obj_id);
    188 				if (in && in->variant_type != oh->type) {
    189 					/* This should not happen, but somehow
    190 					 * Wev'e ended up with an obj_id that
    191 					 * has been reused but not yet deleted,
    192 					 * and worse still it has changed type.
    193 					 * Delete the old object.
    194 					 */
    195 
    196 					yaffs_del_obj(in);
    197 					in = NULL;
    198 				}
    199 
    200 				in = yaffs_find_or_create_by_number(dev,
    201 								tags.obj_id,
    202 								oh->type);
    203 
    204 				if (!in)
    205 					alloc_failed = 1;
    206 
    207 				if (in && oh->shadows_obj > 0) {
    208 
    209 					struct yaffs_shadow_fixer *fixer;
    210 					fixer =
    211 						kmalloc(sizeof
    212 						(struct yaffs_shadow_fixer),
    213 						GFP_NOFS);
    214 					if (fixer) {
    215 						fixer->next = shadow_fixers;
    216 						shadow_fixers = fixer;
    217 						fixer->obj_id = tags.obj_id;
    218 						fixer->shadowed_id =
    219 						    oh->shadows_obj;
    220 						yaffs_trace(YAFFS_TRACE_SCAN,
    221 							" Shadow fixer: %d shadows %d",
    222 							fixer->obj_id,
    223 							fixer->shadowed_id);
    224 
    225 					}
    226 
    227 				}
    228 
    229 				if (in && in->valid) {
    230 					/* We have already filled this one.
    231 					 * We have a duplicate and need to
    232 					 * resolve it. */
    233 
    234 					unsigned existing_serial = in->serial;
    235 					unsigned new_serial =
    236 					    tags.serial_number;
    237 
    238 					if (((existing_serial + 1) & 3) ==
    239 					    new_serial) {
    240 						/* Use new one - destroy the
    241 						 * exisiting one */
    242 						yaffs_chunk_del(dev,
    243 								in->hdr_chunk,
    244 								1, __LINE__);
    245 						in->valid = 0;
    246 					} else {
    247 						/* Use existing - destroy
    248 						 * this one. */
    249 						yaffs_chunk_del(dev, chunk, 1,
    250 								__LINE__);
    251 					}
    252 				}
    253 
    254 				if (in && !in->valid &&
    255 				    (tags.obj_id == YAFFS_OBJECTID_ROOT ||
    256 				     tags.obj_id ==
    257 				     YAFFS_OBJECTID_LOSTNFOUND)) {
    258 					/* We only load some info, don't fiddle
    259 					 * with directory structure */
    260 					in->valid = 1;
    261 					in->variant_type = oh->type;
    262 
    263 					in->yst_mode = oh->yst_mode;
    264 					yaffs_load_attribs(in, oh);
    265 					in->hdr_chunk = chunk;
    266 					in->serial = tags.serial_number;
    267 
    268 				} else if (in && !in->valid) {
    269 					/* we need to load this info */
    270 
    271 					in->valid = 1;
    272 					in->variant_type = oh->type;
    273 
    274 					in->yst_mode = oh->yst_mode;
    275 					yaffs_load_attribs(in, oh);
    276 					in->hdr_chunk = chunk;
    277 					in->serial = tags.serial_number;
    278 
    279 					yaffs_set_obj_name_from_oh(in, oh);
    280 					in->dirty = 0;
    281 
    282 					/* directory stuff...
    283 					 * hook up to parent
    284 					 */
    285 
    286 					parent =
    287 					    yaffs_find_or_create_by_number
    288 					    (dev, oh->parent_obj_id,
    289 					     YAFFS_OBJECT_TYPE_DIRECTORY);
    290 					if (!parent)
    291 						alloc_failed = 1;
    292 					if (parent && parent->variant_type ==
    293 					    YAFFS_OBJECT_TYPE_UNKNOWN) {
    294 						/* Set up as a directory */
    295 						parent->variant_type =
    296 						    YAFFS_OBJECT_TYPE_DIRECTORY;
    297 						INIT_LIST_HEAD(&parent->
    298 							variant.dir_variant.
    299 							children);
    300 					} else if (!parent ||
    301 						parent->variant_type !=
    302 						YAFFS_OBJECT_TYPE_DIRECTORY) {
    303 						/* Hoosterman, a problem....
    304 						 * We're trying to use a
    305 						 * non-directory as a directory
    306 						 */
    307 
    308 						yaffs_trace(YAFFS_TRACE_ERROR,
    309 							"yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
    310 							);
    311 						parent = dev->lost_n_found;
    312 					}
    313 
    314 					yaffs_add_obj_to_dir(parent, in);
    315 
    316 					switch (in->variant_type) {
    317 					case YAFFS_OBJECT_TYPE_UNKNOWN:
    318 						/* Todo got a problem */
    319 						break;
    320 					case YAFFS_OBJECT_TYPE_FILE:
    321 						if (dev->param.
    322 						    use_header_file_size)
    323 							in->variant.
    324 							file_variant.file_size
    325 							= yaffs_oh_to_size(oh);
    326 						break;
    327 					case YAFFS_OBJECT_TYPE_HARDLINK:
    328 						in->variant.
    329 						    hardlink_variant.equiv_id =
    330 						    oh->equiv_id;
    331 						list_add(&in->hard_links,
    332 								&hard_list);
    333 						break;
    334 					case YAFFS_OBJECT_TYPE_DIRECTORY:
    335 						/* Do nothing */
    336 						break;
    337 					case YAFFS_OBJECT_TYPE_SPECIAL:
    338 						/* Do nothing */
    339 						break;
    340 					case YAFFS_OBJECT_TYPE_SYMLINK:
    341 						in->variant.symlink_variant.
    342 						    alias =
    343 						    yaffs_clone_str(oh->alias);
    344 						if (!in->variant.
    345 						    symlink_variant.alias)
    346 							alloc_failed = 1;
    347 						break;
    348 					}
    349 				}
    350 			}
    351 		}
    352 
    353 		if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
    354 			/* If we got this far while scanning,
    355 			 * then the block is fully allocated. */
    356 			state = YAFFS_BLOCK_STATE_FULL;
    357 		}
    358 
    359 		if (state == YAFFS_BLOCK_STATE_ALLOCATING) {
    360 			/* If the block was partially allocated then
    361 			 * treat it as fully allocated. */
    362 			state = YAFFS_BLOCK_STATE_FULL;
    363 			dev->alloc_block = -1;
    364 		}
    365 
    366 		bi->block_state = state;
    367 
    368 		/* Now let's see if it was dirty */
    369 		if (bi->pages_in_use == 0 &&
    370 		    !bi->has_shrink_hdr &&
    371 		    bi->block_state == YAFFS_BLOCK_STATE_FULL)
    372 			yaffs_block_became_dirty(dev, blk);
    373 	}
    374 
    375 	/* Ok, we've done all the scanning.
    376 	 * Fix up the hard link chains.
    377 	 * We should now have scanned all the objects, now it's time to add
    378 	 * these hardlinks.
    379 	 */
    380 
    381 	yaffs_link_fixup(dev, &hard_list);
    382 
    383 	/*
    384 	 * Fix up any shadowed objects.
    385 	 * There should not be more than one of these.
    386 	 */
    387 	{
    388 		struct yaffs_shadow_fixer *fixer;
    389 		struct yaffs_obj *obj;
    390 
    391 		while (shadow_fixers) {
    392 			fixer = shadow_fixers;
    393 			shadow_fixers = fixer->next;
    394 			/* Complete the rename transaction by deleting the
    395 			 * shadowed object then setting the object header
    396 			 to unshadowed.
    397 			 */
    398 			obj = yaffs_find_by_number(dev, fixer->shadowed_id);
    399 			if (obj)
    400 				yaffs_del_obj(obj);
    401 
    402 			obj = yaffs_find_by_number(dev, fixer->obj_id);
    403 
    404 			if (obj)
    405 				yaffs_update_oh(obj, NULL, 1, 0, 0, NULL);
    406 
    407 			kfree(fixer);
    408 		}
    409 	}
    410 
    411 	yaffs_release_temp_buffer(dev, chunk_data);
    412 
    413 	if (alloc_failed)
    414 		return YAFFS_FAIL;
    415 
    416 	yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends");
    417 
    418 	return YAFFS_OK;
    419 }
    420