diff options
Diffstat (limited to 'fs/yaffs2/yaffs_guts.c')
| -rw-r--r-- | fs/yaffs2/yaffs_guts.c | 9119 | 
1 files changed, 3411 insertions, 5708 deletions
| diff --git a/fs/yaffs2/yaffs_guts.c b/fs/yaffs2/yaffs_guts.c index 7390b40e0..00d1c5a91 100644 --- a/fs/yaffs2/yaffs_guts.c +++ b/fs/yaffs2/yaffs_guts.c @@ -1,7 +1,7 @@  /*   * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.   * - * Copyright (C) 2002-2007 Aleph One Ltd. + * Copyright (C) 2002-2011 Aleph One Ltd.   *   for Toby Churchill Ltd and Brightstar Engineering   *   * Created by Charles Manning <charles@aleph1.co.uk> @@ -11,259 +11,176 @@   * published by the Free Software Foundation.   */ -/* XXX U-BOOT XXX */ -#include <common.h> - -const char *yaffs_guts_c_version = -    "$Id: yaffs_guts.c,v 1.52 2007/10/16 00:45:05 charles Exp $"; -  #include "yportenv.h" -#include "linux/stat.h" +#include "yaffs_trace.h" -#include "yaffsinterface.h" -#include "yaffsfs.h"  #include "yaffs_guts.h" -#include "yaffs_tagsvalidity.h" - +#include "yaffs_getblockinfo.h"  #include "yaffs_tagscompat.h" -#ifndef  CONFIG_YAFFS_USE_OWN_SORT -#include "yaffs_qsort.h" -#endif  #include "yaffs_nand.h" - -#include "yaffs_checkptrw.h" - +#include "yaffs_yaffs1.h" +#include "yaffs_yaffs2.h" +#include "yaffs_bitmap.h" +#include "yaffs_verify.h"  #include "yaffs_nand.h"  #include "yaffs_packedtags2.h" +#include "yaffs_nameval.h" +#include "yaffs_allocator.h" +#include "yaffs_attribs.h" +#include "yaffs_summary.h" -#include "malloc.h" - -#ifdef CONFIG_YAFFS_WINCE -void yfsd_LockYAFFS(BOOL fsLockOnly); -void yfsd_UnlockYAFFS(BOOL fsLockOnly); -#endif - -#define YAFFS_PASSIVE_GC_CHUNKS 2 +/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ +#define YAFFS_GC_GOOD_ENOUGH 2 +#define YAFFS_GC_PASSIVE_THRESHOLD 4  #include "yaffs_ecc.h" +/* Forward declarations */ -/* Robustification (if it ever comes about...) */ -static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND); -static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk); -static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, -				     const __u8 * data, -				     const yaffs_ExtendedTags * tags); -static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, -				    const yaffs_ExtendedTags * tags); - -/* Other local prototypes */ -static int yaffs_UnlinkObject( yaffs_Object *obj); -static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj); - -static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList); - -static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev, -					     const __u8 * buffer, -					     yaffs_ExtendedTags * tags, -					     int useReserve); -static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, -				  int chunkInNAND, int inScan); - -static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, -					   yaffs_ObjectType type); -static void yaffs_AddObjectToDirectory(yaffs_Object * directory, -				       yaffs_Object * obj); -static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, -				    int force, int isShrink, int shadows); -static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj); -static int yaffs_CheckStructures(void); -static int yaffs_DoGenericObjectDeletion(yaffs_Object * in); - -static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo); - -static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo); -static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, -				    int lineNo); - -static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, -				  int chunkInNAND); - -static int yaffs_UnlinkWorker(yaffs_Object * obj); -static void yaffs_DestroyObject(yaffs_Object * obj); - -static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, -			   int chunkInObject); +static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, +			     const u8 *buffer, int n_bytes, int use_reserve); -loff_t yaffs_GetFileSize(yaffs_Object * obj); -static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr); -static void yaffs_VerifyFreeChunks(yaffs_Device * dev); - -static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in); - -#ifdef YAFFS_PARANOID -static int yaffs_CheckFileSanity(yaffs_Object * in); -#else -#define yaffs_CheckFileSanity(in) -#endif - -static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in); -static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId); +/* Function to calculate chunk and offset */ -static void yaffs_InvalidateCheckpoint(yaffs_Device *dev); +void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, +				int *chunk_out, u32 *offset_out) +{ +	int chunk; +	u32 offset; -static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, -				 yaffs_ExtendedTags * tags); +	chunk = (u32) (addr >> dev->chunk_shift); -static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos); -static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev, -					  yaffs_FileStructure * fStruct, -					  __u32 chunkId); +	if (dev->chunk_div == 1) { +		/* easy power of 2 case */ +		offset = (u32) (addr & dev->chunk_mask); +	} else { +		/* Non power-of-2 case */ +		loff_t chunk_base; -/* Function to calculate chunk and offset */ +		chunk /= dev->chunk_div; -static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, __u32 *chunk, __u32 *offset) -{ -	if(dev->chunkShift){ -		/* Easy-peasy power of 2 case */ -		*chunk  = (__u32)(addr >> dev->chunkShift); -		*offset = (__u32)(addr & dev->chunkMask); +		chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; +		offset = (u32) (addr - chunk_base);  	} -	else if(dev->crumbsPerChunk) -	{ -		/* Case where we're using "crumbs" */ -		*offset = (__u32)(addr & dev->crumbMask); -		addr >>= dev->crumbShift; -		*chunk = ((__u32)addr)/dev->crumbsPerChunk; -		*offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift); -	} -	else -		YBUG(); + +	*chunk_out = chunk; +	*offset_out = offset;  } -/* Function to return the number of shifts for a power of 2 greater than or equal - * to the given number +/* Function to return the number of shifts for a power of 2 greater than or + * equal to the given number   * Note we don't try to cater for all possible numbers and this does not have to   * be hellishly efficient.   */ -static __u32 ShiftsGE(__u32 x) +static inline u32 calc_shifts_ceiling(u32 x)  { -	int extraBits; -	int nShifts; +	int extra_bits; +	int shifts; -	nShifts = extraBits = 0; +	shifts = extra_bits = 0; -	while(x>1){ -		if(x & 1) extraBits++; -		x>>=1; -		nShifts++; +	while (x > 1) { +		if (x & 1) +			extra_bits++; +		x >>= 1; +		shifts++;  	} -	if(extraBits) -		nShifts++; +	if (extra_bits) +		shifts++; -	return nShifts; +	return shifts;  }  /* Function to return the number of shifts to get a 1 in bit 0   */ -static __u32 ShiftDiv(__u32 x) +static inline u32 calc_shifts(u32 x)  { -	int nShifts; +	u32 shifts; -	nShifts =  0; +	shifts = 0; -	if(!x) return 0; +	if (!x) +		return 0; -	while( !(x&1)){ -		x>>=1; -		nShifts++; +	while (!(x & 1)) { +		x >>= 1; +		shifts++;  	} -	return nShifts; +	return shifts;  } - -  /*   * Temporary buffer manipulations.   */ -static int yaffs_InitialiseTempBuffers(yaffs_Device *dev) +static int yaffs_init_tmp_buffers(struct yaffs_dev *dev)  {  	int i; -	__u8 *buf = (__u8 *)1; +	u8 *buf = (u8 *) 1; -	memset(dev->tempBuffer,0,sizeof(dev->tempBuffer)); +	memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer));  	for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { -		dev->tempBuffer[i].line = 0;	/* not in use */ -		dev->tempBuffer[i].buffer = buf = -		    YMALLOC_DMA(dev->nDataBytesPerChunk); +		dev->temp_buffer[i].in_use = 0; +		buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); +		dev->temp_buffer[i].buffer = buf;  	}  	return buf ? YAFFS_OK : YAFFS_FAIL; -  } -static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo) +u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev)  { -	int i, j; -	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -		if (dev->tempBuffer[i].line == 0) { -			dev->tempBuffer[i].line = lineNo; -			if ((i + 1) > dev->maxTemp) { -				dev->maxTemp = i + 1; -				for (j = 0; j <= i; j++) -					dev->tempBuffer[j].maxLine = -					    dev->tempBuffer[j].line; -			} +	int i; -			return dev->tempBuffer[i].buffer; -		} -	} +	dev->temp_in_use++; +	if (dev->temp_in_use > dev->max_temp) +		dev->max_temp = dev->temp_in_use; -	T(YAFFS_TRACE_BUFFERS, -	  (TSTR("Out of temp buffers at line %d, other held by lines:"), -	   lineNo));  	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -		T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line)); +		if (dev->temp_buffer[i].in_use == 0) { +			dev->temp_buffer[i].in_use = 1; +			return dev->temp_buffer[i].buffer; +		}  	} -	T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR))); +	yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers");  	/*  	 * If we got here then we have to allocate an unmanaged one  	 * This is not good.  	 */ -	dev->unmanagedTempAllocations++; -	return YMALLOC(dev->nDataBytesPerChunk); +	dev->unmanaged_buffer_allocs++; +	return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS);  } -static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, -				    int lineNo) +void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer)  {  	int i; + +	dev->temp_in_use--; +  	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -		if (dev->tempBuffer[i].buffer == buffer) { -			dev->tempBuffer[i].line = 0; +		if (dev->temp_buffer[i].buffer == buffer) { +			dev->temp_buffer[i].in_use = 0;  			return;  		}  	}  	if (buffer) {  		/* assume it is an unmanaged one. */ -		T(YAFFS_TRACE_BUFFERS, -		  (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR), -		   lineNo)); -		YFREE(buffer); -		dev->unmanagedTempDeallocations++; +		yaffs_trace(YAFFS_TRACE_BUFFERS, +			"Releasing unmanaged temp buffer"); +		kfree(buffer); +		dev->unmanaged_buffer_deallocs++;  	}  } @@ -271,621 +188,339 @@ static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,  /*   * Determine if we have a managed buffer.   */ -int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer) +int yaffs_is_managed_tmp_buffer(struct yaffs_dev *dev, const u8 *buffer)  {  	int i; +  	for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -		if (dev->tempBuffer[i].buffer == buffer) +		if (dev->temp_buffer[i].buffer == buffer)  			return 1; -  	} -    for (i = 0; i < dev->nShortOpCaches; i++) { -	if( dev->srCache[i].data == buffer ) -	    return 1; - -    } +	for (i = 0; i < dev->param.n_caches; i++) { +		if (dev->cache[i].data == buffer) +			return 1; +	} -    if (buffer == dev->checkpointBuffer) -      return 1; +	if (buffer == dev->checkpt_buffer) +		return 1; -    T(YAFFS_TRACE_ALWAYS, -	  (TSTR("yaffs: unmaged buffer detected.\n" TENDSTR))); -    return 0; +	yaffs_trace(YAFFS_TRACE_ALWAYS, +	  "yaffs: unmaged buffer detected."); +	return 0;  } - -  /* - * Chunk bitmap manipulations + * Functions for robustisizing TODO + *   */ -static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk) +static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, +				     const u8 *data, +				     const struct yaffs_ext_tags *tags)  { -	if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { -		T(YAFFS_TRACE_ERROR, -		  (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), -		   blk)); -		YBUG(); -	} -	return dev->chunkBits + -	    (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); +	dev = dev; +	nand_chunk = nand_chunk; +	data = data; +	tags = tags;  } -static Y_INLINE void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk) +static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, +				      const struct yaffs_ext_tags *tags)  { -	if(blk < dev->internalStartBlock || blk > dev->internalEndBlock || -	   chunk < 0 || chunk >= dev->nChunksPerBlock) { -	   T(YAFFS_TRACE_ERROR, -	    (TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR),blk,chunk)); -	    YBUG(); -	} +	dev = dev; +	nand_chunk = nand_chunk; +	tags = tags;  } -static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk) +void yaffs_handle_chunk_error(struct yaffs_dev *dev, +			      struct yaffs_block_info *bi)  { -	__u8 *blkBits = yaffs_BlockBits(dev, blk); +	if (!bi->gc_prioritise) { +		bi->gc_prioritise = 1; +		dev->has_pending_prioritised_gc = 1; +		bi->chunk_error_strikes++; -	memset(blkBits, 0, dev->chunkBitmapStride); -} +		if (bi->chunk_error_strikes > 3) { +			bi->needs_retiring = 1;	/* Too many stikes, so retire */ +			yaffs_trace(YAFFS_TRACE_ALWAYS, +				"yaffs: Block struck out"); -static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk) -{ -	__u8 *blkBits = yaffs_BlockBits(dev, blk); - -	yaffs_VerifyChunkBitId(dev,blk,chunk); - -	blkBits[chunk / 8] &= ~(1 << (chunk & 7)); -} - -static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk) -{ -	__u8 *blkBits = yaffs_BlockBits(dev, blk); - -	yaffs_VerifyChunkBitId(dev,blk,chunk); - -	blkBits[chunk / 8] |= (1 << (chunk & 7)); +		} +	}  } -static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk) +static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, +					int erased_ok)  { -	__u8 *blkBits = yaffs_BlockBits(dev, blk); -	yaffs_VerifyChunkBitId(dev,blk,chunk); +	int flash_block = nand_chunk / dev->param.chunks_per_block; +	struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -	return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; -} +	yaffs_handle_chunk_error(dev, bi); -static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk) -{ -	__u8 *blkBits = yaffs_BlockBits(dev, blk); -	int i; -	for (i = 0; i < dev->chunkBitmapStride; i++) { -		if (*blkBits) -			return 1; -		blkBits++; +	if (erased_ok) { +		/* Was an actual write failure, +		 * so mark the block for retirement.*/ +		bi->needs_retiring = 1; +		yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, +		  "**>> Block %d needs retiring", flash_block);  	} -	return 0; -} -static int yaffs_CountChunkBits(yaffs_Device * dev, int blk) -{ -	__u8 *blkBits = yaffs_BlockBits(dev, blk); -	int i; -	int n = 0; -	for (i = 0; i < dev->chunkBitmapStride; i++) { -		__u8 x = *blkBits; -		while(x){ -			if(x & 1) -				n++; -			x >>=1; -		} - -		blkBits++; -	} -	return n; +	/* Delete the chunk */ +	yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); +	yaffs_skip_rest_of_block(dev);  }  /*   * Verification code   */ -static Y_INLINE int yaffs_SkipVerification(yaffs_Device *dev) -{ -	return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); -} - -static Y_INLINE int yaffs_SkipFullVerification(yaffs_Device *dev) -{ -	return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); -} +/* + *  Simple hash function. Needs to have a reasonable spread + */ -static Y_INLINE int yaffs_SkipNANDVerification(yaffs_Device *dev) +static inline int yaffs_hash_fn(int n)  { -	return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); +	if (n < 0) +		n = -n; +	return n % YAFFS_NOBJECT_BUCKETS;  } -static const char * blockStateName[] = { -"Unknown", -"Needs scanning", -"Scanning", -"Empty", -"Allocating", -"Full", -"Dirty", -"Checkpoint", -"Collecting", -"Dead" -}; +/* + * Access functions to useful fake objects. + * Note that root might have a presence in NAND if permissions are set. + */ -static void yaffs_VerifyBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +struct yaffs_obj *yaffs_root(struct yaffs_dev *dev)  { -	int actuallyUsed; -	int inUse; - -	if(yaffs_SkipVerification(dev)) -		return; - -	/* Report illegal runtime states */ -	if(bi->blockState <0 || bi->blockState >= YAFFS_NUMBER_OF_BLOCK_STATES) -		T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has undefined state %d"TENDSTR),n,bi->blockState)); - -	switch(bi->blockState){ -	 case YAFFS_BLOCK_STATE_UNKNOWN: -	 case YAFFS_BLOCK_STATE_SCANNING: -	 case YAFFS_BLOCK_STATE_NEEDS_SCANNING: -		T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has bad run-state %s"TENDSTR), -		n,blockStateName[bi->blockState])); -	} - -	/* Check pages in use and soft deletions are legal */ - -	actuallyUsed = bi->pagesInUse - bi->softDeletions; - -	if(bi->pagesInUse < 0 || bi->pagesInUse > dev->nChunksPerBlock || -	   bi->softDeletions < 0 || bi->softDeletions > dev->nChunksPerBlock || -	   actuallyUsed < 0 || actuallyUsed > dev->nChunksPerBlock) -		T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has illegal values pagesInUsed %d softDeletions %d"TENDSTR), -		n,bi->pagesInUse,bi->softDeletions)); - - -	/* Check chunk bitmap legal */ -	inUse = yaffs_CountChunkBits(dev,n); -	if(inUse != bi->pagesInUse) -		T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has inconsistent values pagesInUse %d counted chunk bits %d"TENDSTR), -			n,bi->pagesInUse,inUse)); - -	/* Check that the sequence number is valid. -	 * Ten million is legal, but is very unlikely -	 */ -	if(dev->isYaffs2 && -	   (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || bi->blockState == YAFFS_BLOCK_STATE_FULL) && -	   (bi->sequenceNumber < YAFFS_LOWEST_SEQUENCE_NUMBER || bi->sequenceNumber > 10000000 )) -		T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has suspect sequence number of %d"TENDSTR), -		n,bi->sequenceNumber)); - +	return dev->root_dir;  } -static void yaffs_VerifyCollectedBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev)  { -	yaffs_VerifyBlock(dev,bi,n); +	return dev->lost_n_found; +} -	/* After collection the block should be in the erased state */ -	/* TODO: This will need to change if we do partial gc */ +/* + *  Erased NAND checking functions + */ -	if(bi->blockState != YAFFS_BLOCK_STATE_EMPTY){ -		T(YAFFS_TRACE_ERROR,(TSTR("Block %d is in state %d after gc, should be erased"TENDSTR), -			n,bi->blockState)); +int yaffs_check_ff(u8 *buffer, int n_bytes) +{ +	/* Horrible, slow implementation */ +	while (n_bytes--) { +		if (*buffer != 0xff) +			return 0; +		buffer++;  	} +	return 1;  } -static void yaffs_VerifyBlocks(yaffs_Device *dev) +static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk)  { -	int i; -	int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; -	int nIllegalBlockStates = 0; - - -	if(yaffs_SkipVerification(dev)) -		return; - -	memset(nBlocksPerState,0,sizeof(nBlocksPerState)); - +	int retval = YAFFS_OK; +	u8 *data = yaffs_get_temp_buffer(dev); +	struct yaffs_ext_tags tags; +	int result; -	for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++){ -		yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); -		yaffs_VerifyBlock(dev,bi,i); +	result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); -		if(bi->blockState >=0 && bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES) -			nBlocksPerState[bi->blockState]++; -		else -			nIllegalBlockStates++; +	if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) +		retval = YAFFS_FAIL; +	if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || +		tags.chunk_used) { +		yaffs_trace(YAFFS_TRACE_NANDACCESS, +			"Chunk %d not erased", nand_chunk); +		retval = YAFFS_FAIL;  	} -	T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); -	T(YAFFS_TRACE_VERIFY,(TSTR("Block summary"TENDSTR))); - -	T(YAFFS_TRACE_VERIFY,(TSTR("%d blocks have illegal states"TENDSTR),nIllegalBlockStates)); -	if(nBlocksPerState[YAFFS_BLOCK_STATE_ALLOCATING] > 1) -		T(YAFFS_TRACE_VERIFY,(TSTR("Too many allocating blocks"TENDSTR))); - -	for(i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) -		T(YAFFS_TRACE_VERIFY, -		  (TSTR("%s %d blocks"TENDSTR), -		  blockStateName[i],nBlocksPerState[i])); - -	if(dev->blocksInCheckpoint != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]) -		T(YAFFS_TRACE_VERIFY, -		 (TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR), -		 dev->blocksInCheckpoint, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])); +	yaffs_release_temp_buffer(dev, data); -	if(dev->nErasedBlocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]) -		T(YAFFS_TRACE_VERIFY, -		 (TSTR("Erased block count wrong dev %d count %d"TENDSTR), -		 dev->nErasedBlocks, nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY])); - -	if(nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING] > 1) -		T(YAFFS_TRACE_VERIFY, -		 (TSTR("Too many collecting blocks %d (max is 1)"TENDSTR), -		 nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING])); - -	T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); +	return retval;  } -/* - * Verify the object header. oh must be valid, but obj and tags may be NULL in which - * case those tests will not be performed. - */ -static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck) +static int yaffs_verify_chunk_written(struct yaffs_dev *dev, +				      int nand_chunk, +				      const u8 *data, +				      struct yaffs_ext_tags *tags)  { -	if(yaffs_SkipVerification(obj->myDev)) -		return; - -	if(!(tags && obj && oh)){ -		T(YAFFS_TRACE_VERIFY, -				(TSTR("Verifying object header tags %x obj %x oh %x"TENDSTR), -				(__u32)tags,(__u32)obj,(__u32)oh)); -		return; -	} - -	if(oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || -	   oh->type > YAFFS_OBJECT_TYPE_MAX) -		T(YAFFS_TRACE_VERIFY, -		 (TSTR("Obj %d header type is illegal value 0x%x"TENDSTR), -		 tags->objectId, oh->type)); - -	if(tags->objectId != obj->objectId) -		T(YAFFS_TRACE_VERIFY, -		 (TSTR("Obj %d header mismatch objectId %d"TENDSTR), -		 tags->objectId, obj->objectId)); +	int retval = YAFFS_OK; +	struct yaffs_ext_tags temp_tags; +	u8 *buffer = yaffs_get_temp_buffer(dev); +	int result; +	result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); +	if (memcmp(buffer, data, dev->data_bytes_per_chunk) || +	    temp_tags.obj_id != tags->obj_id || +	    temp_tags.chunk_id != tags->chunk_id || +	    temp_tags.n_bytes != tags->n_bytes) +		retval = YAFFS_FAIL; -	/* -	 * Check that the object's parent ids match if parentCheck requested. -	 * -	 * Tests do not apply to the root object. -	 */ +	yaffs_release_temp_buffer(dev, buffer); -	if(parentCheck && tags->objectId > 1 && !obj->parent) -		T(YAFFS_TRACE_VERIFY, -		 (TSTR("Obj %d header mismatch parentId %d obj->parent is NULL"TENDSTR), -		 tags->objectId, oh->parentObjectId)); +	return retval; +} -	if(parentCheck && obj->parent && -	   oh->parentObjectId != obj->parent->objectId && -	   (oh->parentObjectId != YAFFS_OBJECTID_UNLINKED || -	    obj->parent->objectId != YAFFS_OBJECTID_DELETED)) -		T(YAFFS_TRACE_VERIFY, -		 (TSTR("Obj %d header mismatch parentId %d parentObjectId %d"TENDSTR), -		 tags->objectId, oh->parentObjectId, obj->parent->objectId)); +int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) +{ +	int reserved_chunks; +	int reserved_blocks = dev->param.n_reserved_blocks; +	int checkpt_blocks; +	checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); -	if(tags->objectId > 1 && oh->name[0] == 0) /* Null name */ -		T(YAFFS_TRACE_VERIFY, -		(TSTR("Obj %d header name is NULL"TENDSTR), -		 obj->objectId)); +	reserved_chunks = +	    (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; -	if(tags->objectId > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */ -		T(YAFFS_TRACE_VERIFY, -		(TSTR("Obj %d header name is 0xFF"TENDSTR), -		 obj->objectId)); +	return (dev->n_free_chunks > (reserved_chunks + n_chunks));  } -static void yaffs_VerifyFile(yaffs_Object *obj) +static int yaffs_find_alloc_block(struct yaffs_dev *dev)  { -	int requiredTallness; -	int actualTallness; -	__u32 lastChunk; -	__u32 x; -	__u32 i; -	yaffs_Device *dev; -	yaffs_ExtendedTags tags; -	yaffs_Tnode *tn; -	__u32 objectId; - -	if(obj && yaffs_SkipVerification(obj->myDev)) -		return; +	int i; +	struct yaffs_block_info *bi; -	dev = obj->myDev; -	objectId = obj->objectId; +	if (dev->n_erased_blocks < 1) { +		/* Hoosterman we've got a problem. +		 * Can't get space to gc +		 */ +		yaffs_trace(YAFFS_TRACE_ERROR, +		  "yaffs tragedy: no more erased blocks"); -	/* Check file size is consistent with tnode depth */ -	lastChunk =  obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1; -	x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS; -	requiredTallness = 0; -	while (x> 0) { -		x >>= YAFFS_TNODES_INTERNAL_BITS; -		requiredTallness++; +		return -1;  	} -	actualTallness = obj->variant.fileVariant.topLevel; - -	if(requiredTallness > actualTallness ) -		T(YAFFS_TRACE_VERIFY, -		(TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR), -		 obj->objectId,actualTallness, requiredTallness)); - - -	/* Check that the chunks in the tnode tree are all correct. -	 * We do this by scanning through the tnode tree and -	 * checking the tags for every chunk match. -	 */ +	/* Find an empty block. */ -	if(yaffs_SkipNANDVerification(dev)) -		return; +	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { +		dev->alloc_block_finder++; +		if (dev->alloc_block_finder < dev->internal_start_block +		    || dev->alloc_block_finder > dev->internal_end_block) { +			dev->alloc_block_finder = dev->internal_start_block; +		} -	for(i = 1; i <= lastChunk; i++){ -		tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant,i); +		bi = yaffs_get_block_info(dev, dev->alloc_block_finder); -		if (tn) { -			__u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); -			if(theChunk > 0){ -				/* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */ -				yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); -				if(tags.objectId != objectId || tags.chunkId != i){ -					T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), -						objectId, i, theChunk, -						tags.objectId, tags.chunkId)); -				} -			} +		if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { +			bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; +			dev->seq_number++; +			bi->seq_number = dev->seq_number; +			dev->n_erased_blocks--; +			yaffs_trace(YAFFS_TRACE_ALLOCATE, +			  "Allocated block %d, seq  %d, %d left" , +			   dev->alloc_block_finder, dev->seq_number, +			   dev->n_erased_blocks); +			return dev->alloc_block_finder;  		} -  	} -} - -static void yaffs_VerifyDirectory(yaffs_Object *obj) -{ -	if(obj && yaffs_SkipVerification(obj->myDev)) -		return; - -} - -static void yaffs_VerifyHardLink(yaffs_Object *obj) -{ -	if(obj && yaffs_SkipVerification(obj->myDev)) -		return; - -	/* Verify sane equivalent object */ -} +	yaffs_trace(YAFFS_TRACE_ALWAYS, +		"yaffs tragedy: no more erased blocks, but there should have been %d", +		dev->n_erased_blocks); -static void yaffs_VerifySymlink(yaffs_Object *obj) -{ -	if(obj && yaffs_SkipVerification(obj->myDev)) -		return; - -	/* Verify symlink string */ -} - -static void yaffs_VerifySpecial(yaffs_Object *obj) -{ -	if(obj && yaffs_SkipVerification(obj->myDev)) -		return; +	return -1;  } -static void yaffs_VerifyObject(yaffs_Object *obj) +static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, +			     struct yaffs_block_info **block_ptr)  { -	yaffs_Device *dev; - -	__u32 chunkMin; -	__u32 chunkMax; +	int ret_val; +	struct yaffs_block_info *bi; -	__u32 chunkIdOk; -	__u32 chunkIsLive; - -	if(!obj) -		return; - -	dev = obj->myDev; - -	if(yaffs_SkipVerification(dev)) -		return; - -	/* Check sane object header chunk */ - -	chunkMin = dev->internalStartBlock * dev->nChunksPerBlock; -	chunkMax = (dev->internalEndBlock+1) * dev->nChunksPerBlock - 1; +	if (dev->alloc_block < 0) { +		/* Get next block to allocate off */ +		dev->alloc_block = yaffs_find_alloc_block(dev); +		dev->alloc_page = 0; +	} -	chunkIdOk = (obj->chunkId >= chunkMin && obj->chunkId <= chunkMax); -	chunkIsLive = chunkIdOk && -			yaffs_CheckChunkBit(dev, -					    obj->chunkId / dev->nChunksPerBlock, -					    obj->chunkId % dev->nChunksPerBlock); -	if(!obj->fake && -	    (!chunkIdOk || !chunkIsLive)) { -	   T(YAFFS_TRACE_VERIFY, -	   (TSTR("Obj %d has chunkId %d %s %s"TENDSTR), -	   obj->objectId,obj->chunkId, -	   chunkIdOk ? "" : ",out of range", -	   chunkIsLive || !chunkIdOk ? "" : ",marked as deleted")); +	if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { +		/* No space unless we're allowed to use the reserve. */ +		return -1;  	} -	if(chunkIdOk && chunkIsLive &&!yaffs_SkipNANDVerification(dev)) { -		yaffs_ExtendedTags tags; -		yaffs_ObjectHeader *oh; -		__u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__); +	if (dev->n_erased_blocks < dev->param.n_reserved_blocks +	    && dev->alloc_page == 0) +		yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); -		oh = (yaffs_ObjectHeader *)buffer; +	/* Next page please.... */ +	if (dev->alloc_block >= 0) { +		bi = yaffs_get_block_info(dev, dev->alloc_block); -		yaffs_ReadChunkWithTagsFromNAND(dev, obj->chunkId,buffer, &tags); +		ret_val = (dev->alloc_block * dev->param.chunks_per_block) + +		    dev->alloc_page; +		bi->pages_in_use++; +		yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); -		yaffs_VerifyObjectHeader(obj,oh,&tags,1); +		dev->alloc_page++; -		yaffs_ReleaseTempBuffer(dev,buffer,__LINE__); -	} +		dev->n_free_chunks--; -	/* Verify it has a parent */ -	if(obj && !obj->fake && -	   (!obj->parent || obj->parent->myDev != dev)){ -	   T(YAFFS_TRACE_VERIFY, -	   (TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR), -	   obj->objectId,obj->parent)); -	} +		/* If the block is full set the state to full */ +		if (dev->alloc_page >= dev->param.chunks_per_block) { +			bi->block_state = YAFFS_BLOCK_STATE_FULL; +			dev->alloc_block = -1; +		} -	/* Verify parent is a directory */ -	if(obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY){ -	   T(YAFFS_TRACE_VERIFY, -	   (TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR), -	   obj->objectId,obj->parent->variantType)); -	} +		if (block_ptr) +			*block_ptr = bi; -	switch(obj->variantType){ -	case YAFFS_OBJECT_TYPE_FILE: -		yaffs_VerifyFile(obj); -		break; -	case YAFFS_OBJECT_TYPE_SYMLINK: -		yaffs_VerifySymlink(obj); -		break; -	case YAFFS_OBJECT_TYPE_DIRECTORY: -		yaffs_VerifyDirectory(obj); -		break; -	case YAFFS_OBJECT_TYPE_HARDLINK: -		yaffs_VerifyHardLink(obj); -		break; -	case YAFFS_OBJECT_TYPE_SPECIAL: -		yaffs_VerifySpecial(obj); -		break; -	case YAFFS_OBJECT_TYPE_UNKNOWN: -	default: -		T(YAFFS_TRACE_VERIFY, -		(TSTR("Obj %d has illegaltype %d"TENDSTR), -		obj->objectId,obj->variantType)); -		break; +		return ret_val;  	} +	yaffs_trace(YAFFS_TRACE_ERROR, +		"!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); +	return -1;  } -static void yaffs_VerifyObjects(yaffs_Device *dev) +static int yaffs_get_erased_chunks(struct yaffs_dev *dev)  { -	yaffs_Object *obj; -	int i; -	struct list_head *lh; - -	if(yaffs_SkipVerification(dev)) -		return; - -	/* Iterate through the objects in each hash entry */ - -	 for(i = 0; i <  YAFFS_NOBJECT_BUCKETS; i++){ -		list_for_each(lh, &dev->objectBucket[i].list) { -			if (lh) { -				obj = list_entry(lh, yaffs_Object, hashLink); -				yaffs_VerifyObject(obj); -			} -		} -	 } - -} - - -/* - *  Simple hash function. Needs to have a reasonable spread - */ +	int n; -static Y_INLINE int yaffs_HashFunction(int n) -{ -/* XXX U-BOOT XXX */ -	/*n = abs(n); */ -	if (n < 0) -		n = -n; -	return (n % YAFFS_NOBJECT_BUCKETS); -} +	n = dev->n_erased_blocks * dev->param.chunks_per_block; -/* - * Access functions to useful fake objects - */ +	if (dev->alloc_block > 0) +		n += (dev->param.chunks_per_block - dev->alloc_page); -yaffs_Object *yaffs_Root(yaffs_Device * dev) -{ -	return dev->rootDir; -} +	return n; -yaffs_Object *yaffs_LostNFound(yaffs_Device * dev) -{ -	return dev->lostNFoundDir;  } -  /* - *  Erased NAND checking functions + * yaffs_skip_rest_of_block() skips over the rest of the allocation block + * if we don't want to write to it.   */ - -int yaffs_CheckFF(__u8 * buffer, int nBytes) -{ -	/* Horrible, slow implementation */ -	while (nBytes--) { -		if (*buffer != 0xFF) -			return 0; -		buffer++; -	} -	return 1; -} - -static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, -				  int chunkInNAND) +void yaffs_skip_rest_of_block(struct yaffs_dev *dev)  { +	struct yaffs_block_info *bi; -	int retval = YAFFS_OK; -	__u8 *data = yaffs_GetTempBuffer(dev, __LINE__); -	yaffs_ExtendedTags tags; - -	yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags); - -	if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR) -		retval = YAFFS_FAIL; - - -	if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) { -		T(YAFFS_TRACE_NANDACCESS, -		  (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND)); -		retval = YAFFS_FAIL; +	if (dev->alloc_block > 0) { +		bi = yaffs_get_block_info(dev, dev->alloc_block); +		if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { +			bi->block_state = YAFFS_BLOCK_STATE_FULL; +			dev->alloc_block = -1; +		}  	} - -	yaffs_ReleaseTempBuffer(dev, data, __LINE__); - -	return retval; -  } -static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, -					     const __u8 * data, -					     yaffs_ExtendedTags * tags, -					     int useReserve) +static int yaffs_write_new_chunk(struct yaffs_dev *dev, +				 const u8 *data, +				 struct yaffs_ext_tags *tags, int use_reserver)  {  	int attempts = 0; -	int writeOk = 0; +	int write_ok = 0;  	int chunk; -	yaffs_InvalidateCheckpoint(dev); +	yaffs2_checkpt_invalidate(dev);  	do { -		yaffs_BlockInfo *bi = 0; -		int erasedOk = 0; +		struct yaffs_block_info *bi = 0; +		int erased_ok = 0; -		chunk = yaffs_AllocateChunk(dev, useReserve, &bi); +		chunk = yaffs_alloc_chunk(dev, use_reserver, &bi);  		if (chunk < 0) {  			/* no space */  			break; @@ -897,7 +532,7 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,  		 *  		 * Check the first page we try to write in a block.  		 * If the check passes then we don't need to check any -		 * more.	If the check fails, we check again... +		 * more.        If the check fails, we check again...  		 * If the block has been erased, we don't need to check.  		 *  		 * However, if the block has been prioritised for gc, @@ -909,55 +544,63 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,  		 * chunk due to power loss.  This checking policy should  		 * catch that case with very few checks and thus save a  		 * lot of checks that are most likely not needed. +		 * +		 * Mods to the above +		 * If an erase check fails or the write fails we skip the +		 * rest of the block.  		 */ -		if (bi->gcPrioritise) { -			yaffs_DeleteChunk(dev, chunk, 1, __LINE__); -			/* try another chunk */ -			continue; -		}  		/* let's give it a try */  		attempts++; -#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED -		bi->skipErasedCheck = 0; -#endif -		if (!bi->skipErasedCheck) { -			erasedOk = yaffs_CheckChunkErased(dev, chunk); -			if (erasedOk != YAFFS_OK) { -				T(YAFFS_TRACE_ERROR, -				(TSTR ("**>> yaffs chunk %d was not erased" -				TENDSTR), chunk)); +		if (dev->param.always_check_erased) +			bi->skip_erased_check = 0; + +		if (!bi->skip_erased_check) { +			erased_ok = yaffs_check_chunk_erased(dev, chunk); +			if (erased_ok != YAFFS_OK) { +				yaffs_trace(YAFFS_TRACE_ERROR, +				  "**>> yaffs chunk %d was not erased", +				  chunk); -				/* try another chunk */ +				/* If not erased, delete this one, +				 * skip rest of block and +				 * try another chunk */ +				yaffs_chunk_del(dev, chunk, 1, __LINE__); +				yaffs_skip_rest_of_block(dev);  				continue;  			} -			bi->skipErasedCheck = 1;  		} -		writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, -				data, tags); -		if (writeOk != YAFFS_OK) { -			yaffs_HandleWriteChunkError(dev, chunk, erasedOk); -			/* try another chunk */ +		write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); + +		if (!bi->skip_erased_check) +			write_ok = +			    yaffs_verify_chunk_written(dev, chunk, data, tags); + +		if (write_ok != YAFFS_OK) { +			/* Clean up aborted write, skip to next block and +			 * try another chunk */ +			yaffs_handle_chunk_wr_error(dev, chunk, erased_ok);  			continue;  		} +		bi->skip_erased_check = 1; +  		/* Copy the data into the robustification buffer */ -		yaffs_HandleWriteChunkOk(dev, chunk, data, tags); +		yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); -	} while (writeOk != YAFFS_OK && -		(yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); +	} while (write_ok != YAFFS_OK && +		 (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); -	if(!writeOk) +	if (!write_ok)  		chunk = -1;  	if (attempts > 1) { -		T(YAFFS_TRACE_ERROR, -			(TSTR("**>> yaffs write required %d attempts" TENDSTR), -			attempts)); - -		dev->nRetriedWrites += (attempts - 1); +		yaffs_trace(YAFFS_TRACE_ERROR, +			"**>> yaffs write required %d attempts", +			attempts); +		dev->n_retried_writes += (attempts - 1);  	}  	return chunk; @@ -967,109 +610,97 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,   * Block retiring for handling a broken block.   */ -static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND) +static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block)  { -	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); +	struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -	yaffs_InvalidateCheckpoint(dev); - -	yaffs_MarkBlockBad(dev, blockInNAND); - -	bi->blockState = YAFFS_BLOCK_STATE_DEAD; -	bi->gcPrioritise = 0; -	bi->needsRetiring = 0; - -	dev->nRetiredBlocks++; -} - -/* - * Functions for robustisizing TODO - * - */ +	yaffs2_checkpt_invalidate(dev); -static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, -				     const __u8 * data, -				     const yaffs_ExtendedTags * tags) -{ -} +	yaffs2_clear_oldest_dirty_seq(dev, bi); -static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, -				    const yaffs_ExtendedTags * tags) -{ -} +	if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { +		if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { +			yaffs_trace(YAFFS_TRACE_ALWAYS, +				"yaffs: Failed to mark bad and erase block %d", +				flash_block); +		} else { +			struct yaffs_ext_tags tags; +			int chunk_id = +			    flash_block * dev->param.chunks_per_block; -void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi) -{ -	if(!bi->gcPrioritise){ -		bi->gcPrioritise = 1; -		dev->hasPendingPrioritisedGCs = 1; -		bi->chunkErrorStrikes ++; +			u8 *buffer = yaffs_get_temp_buffer(dev); -		if(bi->chunkErrorStrikes > 3){ -			bi->needsRetiring = 1; /* Too many stikes, so retire this */ -			T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR))); +			memset(buffer, 0xff, dev->data_bytes_per_chunk); +			memset(&tags, 0, sizeof(tags)); +			tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; +			if (dev->param.write_chunk_tags_fn(dev, chunk_id - +							   dev->chunk_offset, +							   buffer, +							   &tags) != YAFFS_OK) +				yaffs_trace(YAFFS_TRACE_ALWAYS, +					"yaffs: Failed to write bad block marker to block %d", +					flash_block); +			yaffs_release_temp_buffer(dev, buffer);  		} -  	} -} -static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk) -{ +	bi->block_state = YAFFS_BLOCK_STATE_DEAD; +	bi->gc_prioritise = 0; +	bi->needs_retiring = 0; -	int blockInNAND = chunkInNAND / dev->nChunksPerBlock; -	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); +	dev->n_retired_blocks++; +} -	yaffs_HandleChunkError(dev,bi); +/*---------------- Name handling functions ------------*/ +static u16 yaffs_calc_name_sum(const YCHAR *name) +{ +	u16 sum = 0; +	u16 i = 1; -	if(erasedOk ) { -		/* Was an actual write failure, so mark the block for retirement  */ -		bi->needsRetiring = 1; -		T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -		  (TSTR("**>> Block %d needs retiring" TENDSTR), blockInNAND)); +	if (!name) +		return 0; +	while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { +		/* 0x1f mask is case insensitive */ +		sum += ((*name) & 0x1f) * i; +		i++; +		name++;  	} - -	/* Delete the chunk */ -	yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); +	return sum;  } - -/*---------------- Name handling functions ------------*/ - -static __u16 yaffs_CalcNameSum(const YCHAR * name) +void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name)  { -	__u16 sum = 0; -	__u16 i = 1; - -	YUCHAR *bname = (YUCHAR *) name; -	if (bname) { -		while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH/2))) { +	memset(obj->short_name, 0, sizeof(obj->short_name)); +	if (name && +		yaffs_strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= +		YAFFS_SHORT_NAME_LENGTH) +		yaffs_strcpy(obj->short_name, name); +	else +		obj->short_name[0] = _Y('\0'); +	obj->sum = yaffs_calc_name_sum(name); +} -#ifdef CONFIG_YAFFS_CASE_INSENSITIVE -			sum += yaffs_toupper(*bname) * i; +void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, +				const struct yaffs_obj_hdr *oh) +{ +#ifdef CONFIG_YAFFS_AUTO_UNICODE +	YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; +	memset(tmp_name, 0, sizeof(tmp_name)); +	yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, +				YAFFS_MAX_NAME_LENGTH + 1); +	yaffs_set_obj_name(obj, tmp_name);  #else -			sum += (*bname) * i; +	yaffs_set_obj_name(obj, oh->name);  #endif -			i++; -			bname++; -		} -	} -	return sum;  } -static void yaffs_SetObjectName(yaffs_Object * obj, const YCHAR * name) +loff_t yaffs_max_file_size(struct yaffs_dev *dev)  { -#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM -	if (name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH) { -		yaffs_strcpy(obj->shortName, name); -	} else { -		obj->shortName[0] = _Y('\0'); -	} -#endif -	obj->sum = yaffs_CalcNameSum(name); +	return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk;  }  /*-------------------- TNODES ------------------- @@ -1079,381 +710,235 @@ static void yaffs_SetObjectName(yaffs_Object * obj, const YCHAR * name)   * in the tnode.   */ -/* yaffs_CreateTnodes creates a bunch more tnodes and - * adds them to the tnode free list. - * Don't use this function directly - */ - -static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes) +struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev)  { -	int i; -	int tnodeSize; -	yaffs_Tnode *newTnodes; -	__u8 *mem; -	yaffs_Tnode *curr; -	yaffs_Tnode *next; -	yaffs_TnodeList *tnl; - -	if (nTnodes < 1) -		return YAFFS_OK; +	struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); -	/* Calculate the tnode size in bytes for variable width tnode support. -	 * Must be a multiple of 32-bits  */ -	tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; - -	/* make these things */ - -	newTnodes = YMALLOC(nTnodes * tnodeSize); -	mem = (__u8 *)newTnodes; - -	if (!newTnodes) { -		T(YAFFS_TRACE_ERROR, -		  (TSTR("yaffs: Could not allocate Tnodes" TENDSTR))); -		return YAFFS_FAIL; -	} - -	/* Hook them into the free list */ -#if 0 -	for (i = 0; i < nTnodes - 1; i++) { -		newTnodes[i].internal[0] = &newTnodes[i + 1]; -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG -		newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; -#endif -	} - -	newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes; -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG -	newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; -#endif -	dev->freeTnodes = newTnodes; -#else -	/* New hookup for wide tnodes */ -	for(i = 0; i < nTnodes -1; i++) { -		curr = (yaffs_Tnode *) &mem[i * tnodeSize]; -		next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize]; -		curr->internal[0] = next; -	} - -	curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize]; -	curr->internal[0] = dev->freeTnodes; -	dev->freeTnodes = (yaffs_Tnode *)mem; - -#endif - - -	dev->nFreeTnodes += nTnodes; -	dev->nTnodesCreated += nTnodes; - -	/* Now add this bunch of tnodes to a list for freeing up. -	 * NB If we can't add this to the management list it isn't fatal -	 * but it just means we can't free this bunch of tnodes later. -	 */ - -	tnl = YMALLOC(sizeof(yaffs_TnodeList)); -	if (!tnl) { -		T(YAFFS_TRACE_ERROR, -		  (TSTR -		   ("yaffs: Could not add tnodes to management list" TENDSTR))); -		   return YAFFS_FAIL; - -	} else { -		tnl->tnodes = newTnodes; -		tnl->next = dev->allocatedTnodeList; -		dev->allocatedTnodeList = tnl; -	} - -	T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR))); - -	return YAFFS_OK; -} - -/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */ - -static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev) -{ -	yaffs_Tnode *tn = NULL; - -	/* If there are none left make more */ -	if (!dev->freeTnodes) { -		yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES); -	} - -	if (dev->freeTnodes) { -		tn = dev->freeTnodes; -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG -		if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) { -			/* Hoosterman, this thing looks like it isn't in the list */ -			T(YAFFS_TRACE_ALWAYS, -			  (TSTR("yaffs: Tnode list bug 1" TENDSTR))); -		} -#endif -		dev->freeTnodes = dev->freeTnodes->internal[0]; -		dev->nFreeTnodes--; +	if (tn) { +		memset(tn, 0, dev->tnode_size); +		dev->n_tnodes++;  	} -	return tn; -} - -static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev) -{ -	yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev); - -	if(tn) -		memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); +	dev->checkpoint_blocks_required = 0;	/* force recalculation */  	return tn;  }  /* FreeTnode frees up a tnode and puts it back on the free list */ -static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn) +static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)  { -	if (tn) { -#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG -		if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) { -			/* Hoosterman, this thing looks like it is already in the list */ -			T(YAFFS_TRACE_ALWAYS, -			  (TSTR("yaffs: Tnode list bug 2" TENDSTR))); -		} -		tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1; -#endif -		tn->internal[0] = dev->freeTnodes; -		dev->freeTnodes = tn; -		dev->nFreeTnodes++; -	} +	yaffs_free_raw_tnode(dev, tn); +	dev->n_tnodes--; +	dev->checkpoint_blocks_required = 0;	/* force recalculation */  } -static void yaffs_DeinitialiseTnodes(yaffs_Device * dev) +static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev)  { -	/* Free the list of allocated tnodes */ -	yaffs_TnodeList *tmp; - -	while (dev->allocatedTnodeList) { -		tmp = dev->allocatedTnodeList->next; - -		YFREE(dev->allocatedTnodeList->tnodes); -		YFREE(dev->allocatedTnodeList); -		dev->allocatedTnodeList = tmp; - -	} - -	dev->freeTnodes = NULL; -	dev->nFreeTnodes = 0; +	yaffs_deinit_raw_tnodes_and_objs(dev); +	dev->n_obj = 0; +	dev->n_tnodes = 0;  } -static void yaffs_InitialiseTnodes(yaffs_Device * dev) +void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, +			unsigned pos, unsigned val)  { -	dev->allocatedTnodeList = NULL; -	dev->freeTnodes = NULL; -	dev->nFreeTnodes = 0; -	dev->nTnodesCreated = 0; - -} +	u32 *map = (u32 *) tn; +	u32 bit_in_map; +	u32 bit_in_word; +	u32 word_in_map; +	u32 mask; +	pos &= YAFFS_TNODES_LEVEL0_MASK; +	val >>= dev->chunk_grp_bits; -void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val) -{ -  __u32 *map = (__u32 *)tn; -  __u32 bitInMap; -  __u32 bitInWord; -  __u32 wordInMap; -  __u32 mask; +	bit_in_map = pos * dev->tnode_width; +	word_in_map = bit_in_map / 32; +	bit_in_word = bit_in_map & (32 - 1); -  pos &= YAFFS_TNODES_LEVEL0_MASK; -  val >>= dev->chunkGroupBits; +	mask = dev->tnode_mask << bit_in_word; -  bitInMap = pos * dev->tnodeWidth; -  wordInMap = bitInMap /32; -  bitInWord = bitInMap & (32 -1); +	map[word_in_map] &= ~mask; +	map[word_in_map] |= (mask & (val << bit_in_word)); -  mask = dev->tnodeMask << bitInWord; - -  map[wordInMap] &= ~mask; -  map[wordInMap] |= (mask & (val << bitInWord)); - -  if(dev->tnodeWidth > (32-bitInWord)) { -    bitInWord = (32 - bitInWord); -    wordInMap++;; -    mask = dev->tnodeMask >> (/*dev->tnodeWidth -*/ bitInWord); -    map[wordInMap] &= ~mask; -    map[wordInMap] |= (mask & (val >> bitInWord)); -  } +	if (dev->tnode_width > (32 - bit_in_word)) { +		bit_in_word = (32 - bit_in_word); +		word_in_map++; +		mask = +		    dev->tnode_mask >> bit_in_word; +		map[word_in_map] &= ~mask; +		map[word_in_map] |= (mask & (val >> bit_in_word)); +	}  } -static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) +u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, +			 unsigned pos)  { -  __u32 *map = (__u32 *)tn; -  __u32 bitInMap; -  __u32 bitInWord; -  __u32 wordInMap; -  __u32 val; +	u32 *map = (u32 *) tn; +	u32 bit_in_map; +	u32 bit_in_word; +	u32 word_in_map; +	u32 val; -  pos &= YAFFS_TNODES_LEVEL0_MASK; +	pos &= YAFFS_TNODES_LEVEL0_MASK; -  bitInMap = pos * dev->tnodeWidth; -  wordInMap = bitInMap /32; -  bitInWord = bitInMap & (32 -1); +	bit_in_map = pos * dev->tnode_width; +	word_in_map = bit_in_map / 32; +	bit_in_word = bit_in_map & (32 - 1); -  val = map[wordInMap] >> bitInWord; +	val = map[word_in_map] >> bit_in_word; -  if(dev->tnodeWidth > (32-bitInWord)) { -    bitInWord = (32 - bitInWord); -    wordInMap++;; -    val |= (map[wordInMap] << bitInWord); -  } +	if (dev->tnode_width > (32 - bit_in_word)) { +		bit_in_word = (32 - bit_in_word); +		word_in_map++; +		val |= (map[word_in_map] << bit_in_word); +	} -  val &= dev->tnodeMask; -  val <<= dev->chunkGroupBits; +	val &= dev->tnode_mask; +	val <<= dev->chunk_grp_bits; -  return val; +	return val;  }  /* ------------------- End of individual tnode manipulation -----------------*/  /* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ - * The look up tree is represented by the top tnode and the number of topLevel + * The look up tree is represented by the top tnode and the number of top_level   * in the tree. 0 means only the level 0 tnode is in the tree.   */  /* FindLevel0Tnode finds the level 0 tnode, if one exists. */ -static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev, -					  yaffs_FileStructure * fStruct, -					  __u32 chunkId) +struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, +				       struct yaffs_file_var *file_struct, +				       u32 chunk_id)  { +	struct yaffs_tnode *tn = file_struct->top; +	u32 i; +	int required_depth; +	int level = file_struct->top_level; -	yaffs_Tnode *tn = fStruct->top; -	__u32 i; -	int requiredTallness; -	int level = fStruct->topLevel; +	dev = dev;  	/* Check sane level and chunk Id */ -	if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) { +	if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)  		return NULL; -	} -	if (chunkId > YAFFS_MAX_CHUNK_ID) { +	if (chunk_id > YAFFS_MAX_CHUNK_ID)  		return NULL; -	} -	/* First check we're tall enough (ie enough topLevel) */ +	/* First check we're tall enough (ie enough top_level) */ -	i = chunkId >> YAFFS_TNODES_LEVEL0_BITS; -	requiredTallness = 0; +	i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; +	required_depth = 0;  	while (i) {  		i >>= YAFFS_TNODES_INTERNAL_BITS; -		requiredTallness++; +		required_depth++;  	} -	if (requiredTallness > fStruct->topLevel) { -		/* Not tall enough, so we can't find it, return NULL. */ -		return NULL; -	} +	if (required_depth > file_struct->top_level) +		return NULL;	/* Not tall enough, so we can't find it */  	/* Traverse down to level 0 */  	while (level > 0 && tn) { -		tn = tn-> -		    internal[(chunkId >> -			       ( YAFFS_TNODES_LEVEL0_BITS + -				 (level - 1) * -				 YAFFS_TNODES_INTERNAL_BITS) -			      ) & -			     YAFFS_TNODES_INTERNAL_MASK]; +		tn = tn->internal[(chunk_id >> +				   (YAFFS_TNODES_LEVEL0_BITS + +				    (level - 1) * +				    YAFFS_TNODES_INTERNAL_BITS)) & +				  YAFFS_TNODES_INTERNAL_MASK];  		level--; -  	}  	return tn;  } -/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. +/* add_find_tnode_0 finds the level 0 tnode if it exists, + * otherwise first expands the tree.   * This happens in two steps:   *  1. If the tree isn't tall enough, then make it taller.   *  2. Scan down the tree towards the level 0 tnode adding tnodes if required.   *   * Used when modifying the tree.   * - *  If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will - *  be plugged into the ttree. + *  If the tn argument is NULL, then a fresh tnode will be added otherwise the + *  specified tn will be plugged into the ttree.   */ -static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev, -					       yaffs_FileStructure * fStruct, -					       __u32 chunkId, -					       yaffs_Tnode *passedTn) +struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, +					   struct yaffs_file_var *file_struct, +					   u32 chunk_id, +					   struct yaffs_tnode *passed_tn)  { - -	int requiredTallness; +	int required_depth;  	int i;  	int l; -	yaffs_Tnode *tn; - -	__u32 x; - +	struct yaffs_tnode *tn; +	u32 x;  	/* Check sane level and page Id */ -	if (fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) { +	if (file_struct->top_level < 0 || +	    file_struct->top_level > YAFFS_TNODES_MAX_LEVEL)  		return NULL; -	} -	if (chunkId > YAFFS_MAX_CHUNK_ID) { +	if (chunk_id > YAFFS_MAX_CHUNK_ID)  		return NULL; -	} -	/* First check we're tall enough (ie enough topLevel) */ +	/* First check we're tall enough (ie enough top_level) */ -	x = chunkId >> YAFFS_TNODES_LEVEL0_BITS; -	requiredTallness = 0; +	x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; +	required_depth = 0;  	while (x) {  		x >>= YAFFS_TNODES_INTERNAL_BITS; -		requiredTallness++; +		required_depth++;  	} +	if (required_depth > file_struct->top_level) { +		/* Not tall enough, gotta make the tree taller */ +		for (i = file_struct->top_level; i < required_depth; i++) { -	if (requiredTallness > fStruct->topLevel) { -		/* Not tall enough,gotta make the tree taller */ -		for (i = fStruct->topLevel; i < requiredTallness; i++) { - -			tn = yaffs_GetTnode(dev); +			tn = yaffs_get_tnode(dev);  			if (tn) { -				tn->internal[0] = fStruct->top; -				fStruct->top = tn; +				tn->internal[0] = file_struct->top; +				file_struct->top = tn; +				file_struct->top_level++;  			} else { -				T(YAFFS_TRACE_ERROR, -				  (TSTR("yaffs: no more tnodes" TENDSTR))); +				yaffs_trace(YAFFS_TRACE_ERROR, +					"yaffs: no more tnodes"); +				return NULL;  			}  		} - -		fStruct->topLevel = requiredTallness;  	}  	/* Traverse down to level 0, adding anything we need */ -	l = fStruct->topLevel; -	tn = fStruct->top; +	l = file_struct->top_level; +	tn = file_struct->top; -	if(l > 0) { +	if (l > 0) {  		while (l > 0 && tn) { -			x = (chunkId >> -			     ( YAFFS_TNODES_LEVEL0_BITS + +			x = (chunk_id >> +			     (YAFFS_TNODES_LEVEL0_BITS +  			      (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &  			    YAFFS_TNODES_INTERNAL_MASK; - -			if((l>1) && !tn->internal[x]){ +			if ((l > 1) && !tn->internal[x]) {  				/* Add missing non-level-zero tnode */ -				tn->internal[x] = yaffs_GetTnode(dev); - -			} else if(l == 1) { +				tn->internal[x] = yaffs_get_tnode(dev); +				if (!tn->internal[x]) +					return NULL; +			} else if (l == 1) {  				/* Looking from level 1 at level 0 */ -				if (passedTn) { -					/* If we already have one, then release it.*/ -					if(tn->internal[x]) -						yaffs_FreeTnode(dev,tn->internal[x]); -					tn->internal[x] = passedTn; +				if (passed_tn) { +					/* If we already have one, release it */ +					if (tn->internal[x]) +						yaffs_free_tnode(dev, +							tn->internal[x]); +					tn->internal[x] = passed_tn; -				} else if(!tn->internal[x]) { +				} else if (!tn->internal[x]) {  					/* Don't have one, none passed in */ -					tn->internal[x] = yaffs_GetTnode(dev); +					tn->internal[x] = yaffs_get_tnode(dev); +					if (!tn->internal[x]) +						return NULL;  				}  			} @@ -1462,2979 +947,2479 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev,  		}  	} else {  		/* top is level 0 */ -		if(passedTn) { -			memcpy(tn,passedTn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); -			yaffs_FreeTnode(dev,passedTn); +		if (passed_tn) { +			memcpy(tn, passed_tn, +			       (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); +			yaffs_free_tnode(dev, passed_tn);  		}  	}  	return tn;  } -static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk, -				  yaffs_ExtendedTags * tags, int objectId, -				  int chunkInInode) +static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, +			    int chunk_obj) +{ +	return (tags->chunk_id == chunk_obj && +		tags->obj_id == obj_id && +		!tags->is_deleted) ? 1 : 0; + +} + +static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, +					struct yaffs_ext_tags *tags, int obj_id, +					int inode_chunk)  {  	int j; -	for (j = 0; theChunk && j < dev->chunkGroupSize; j++) { -		if (yaffs_CheckChunkBit -		    (dev, theChunk / dev->nChunksPerBlock, -		     theChunk % dev->nChunksPerBlock)) { -			yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, -							tags); -			if (yaffs_TagsMatch(tags, objectId, chunkInInode)) { -				/* found it; */ -				return theChunk; +	for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { +		if (yaffs_check_chunk_bit +		    (dev, the_chunk / dev->param.chunks_per_block, +		     the_chunk % dev->param.chunks_per_block)) { +			if (dev->chunk_grp_size == 1) +				return the_chunk; +			else { +				yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, +							 tags); +				if (yaffs_tags_match(tags, +							obj_id, inode_chunk)) { +					/* found it; */ +					return the_chunk; +				}  			}  		} -		theChunk++; +		the_chunk++;  	}  	return -1;  } -static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk) +static int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, +				    struct yaffs_ext_tags *tags)  { +	/*Get the Tnode, then get the level 0 offset chunk offset */ +	struct yaffs_tnode *tn; +	int the_chunk = -1; +	struct yaffs_ext_tags local_tags; +	int ret_val = -1; +	struct yaffs_dev *dev = in->my_dev; -	yaffs_BlockInfo *theBlock; - -	T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk)); - -	theBlock = yaffs_GetBlockInfo(dev, chunk / dev->nChunksPerBlock); -	if (theBlock) { -		theBlock->softDeletions++; -		dev->nFreeChunks++; +	if (!tags) { +		/* Passed a NULL, so use our own tags space */ +		tags = &local_tags;  	} -} -/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file. - * All soft deleting does is increment the block's softdelete count and pulls the chunk out - * of the tnode. - * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted. - */ +	tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -static int yaffs_SoftDeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, -				  __u32 level, int chunkOffset) -{ -	int i; -	int theChunk; -	int allDone = 1; -	yaffs_Device *dev = in->myDev; +	if (!tn) +		return ret_val; -	if (tn) { -		if (level > 0) { +	the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -			for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; -			     i--) { -				if (tn->internal[i]) { -					allDone = -					    yaffs_SoftDeleteWorker(in, -								   tn-> -								   internal[i], -								   level - 1, -								   (chunkOffset -								    << -								    YAFFS_TNODES_INTERNAL_BITS) -								   + i); -					if (allDone) { -						yaffs_FreeTnode(dev, -								tn-> -								internal[i]); -						tn->internal[i] = NULL; -					} else { -						/* Hoosterman... how could this happen? */ -					} -				} -			} -			return (allDone) ? 1 : 0; -		} else if (level == 0) { - -			for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { -				theChunk = yaffs_GetChunkGroupBase(dev,tn,i); -				if (theChunk) { -					/* Note this does not find the real chunk, only the chunk group. -					 * We make an assumption that a chunk group is not larger than -					 * a block. -					 */ -					yaffs_SoftDeleteChunk(dev, theChunk); -					yaffs_PutLevel0Tnode(dev,tn,i,0); -				} - -			} -			return 1; +	ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, +					      inode_chunk); +	return ret_val; +} -		} +static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, +				     struct yaffs_ext_tags *tags) +{ +	/* Get the Tnode, then get the level 0 offset chunk offset */ +	struct yaffs_tnode *tn; +	int the_chunk = -1; +	struct yaffs_ext_tags local_tags; +	struct yaffs_dev *dev = in->my_dev; +	int ret_val = -1; +	if (!tags) { +		/* Passed a NULL, so use our own tags space */ +		tags = &local_tags;  	} -	return 1; - -} +	tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -static void yaffs_SoftDeleteFile(yaffs_Object * obj) -{ -	if (obj->deleted && -	    obj->variantType == YAFFS_OBJECT_TYPE_FILE && !obj->softDeleted) { -		if (obj->nDataChunks <= 0) { -			/* Empty file with no duplicate object headers, just delete it immediately */ -			yaffs_FreeTnode(obj->myDev, -					obj->variant.fileVariant.top); -			obj->variant.fileVariant.top = NULL; -			T(YAFFS_TRACE_TRACING, -			  (TSTR("yaffs: Deleting empty file %d" TENDSTR), -			   obj->objectId)); -			yaffs_DoGenericObjectDeletion(obj); -		} else { -			yaffs_SoftDeleteWorker(obj, -					       obj->variant.fileVariant.top, -					       obj->variant.fileVariant. -					       topLevel, 0); -			obj->softDeleted = 1; -		} -	} -} +	if (!tn) +		return ret_val; -/* Pruning removes any part of the file structure tree that is beyond the - * bounds of the file (ie that does not point to chunks). - * - * A file should only get pruned when its size is reduced. - * - * Before pruning, the chunks must be pulled from the tree and the - * level 0 tnode entries must be zeroed out. - * Could also use this for file deletion, but that's probably better handled - * by a special case. - */ +	the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device * dev, yaffs_Tnode * tn, -				      __u32 level, int del0) -{ -	int i; -	int hasData; +	ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, +					      inode_chunk); -	if (tn) { -		hasData = 0; +	/* Delete the entry in the filestructure (if found) */ +	if (ret_val != -1) +		yaffs_load_tnode_0(dev, tn, inode_chunk, 0); -		for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { -			if (tn->internal[i] && level > 0) { -				tn->internal[i] = -				    yaffs_PruneWorker(dev, tn->internal[i], -						      level - 1, -						      (i == 0) ? del0 : 1); -			} +	return ret_val; +} -			if (tn->internal[i]) { -				hasData++; -			} -		} +int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, +			    int nand_chunk, int in_scan) +{ +	/* NB in_scan is zero unless scanning. +	 * For forward scanning, in_scan is > 0; +	 * for backward scanning in_scan is < 0 +	 * +	 * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. +	 */ -		if (hasData == 0 && del0) { -			/* Free and return NULL */ +	struct yaffs_tnode *tn; +	struct yaffs_dev *dev = in->my_dev; +	int existing_cunk; +	struct yaffs_ext_tags existing_tags; +	struct yaffs_ext_tags new_tags; +	unsigned existing_serial, new_serial; -			yaffs_FreeTnode(dev, tn); -			tn = NULL; +	if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { +		/* Just ignore an attempt at putting a chunk into a non-file +		 * during scanning. +		 * If it is not during Scanning then something went wrong! +		 */ +		if (!in_scan) { +			yaffs_trace(YAFFS_TRACE_ERROR, +				"yaffs tragedy:attempt to put data chunk into a non-file" +				); +			BUG();  		} +		yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); +		return YAFFS_OK;  	} -	return tn; - -} +	tn = yaffs_add_find_tnode_0(dev, +				    &in->variant.file_variant, +				    inode_chunk, NULL); +	if (!tn) +		return YAFFS_FAIL; -static int yaffs_PruneFileStructure(yaffs_Device * dev, -				    yaffs_FileStructure * fStruct) -{ -	int i; -	int hasData; -	int done = 0; -	yaffs_Tnode *tn; +	if (!nand_chunk) +		/* Dummy insert, bail now */ +		return YAFFS_OK; -	if (fStruct->topLevel > 0) { -		fStruct->top = -		    yaffs_PruneWorker(dev, fStruct->top, fStruct->topLevel, 0); +	existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); -		/* Now we have a tree with all the non-zero branches NULL but the height -		 * is the same as it was. -		 * Let's see if we can trim internal tnodes to shorten the tree. -		 * We can do this if only the 0th element in the tnode is in use -		 * (ie all the non-zero are NULL) +	if (in_scan != 0) { +		/* If we're scanning then we need to test for duplicates +		 * NB This does not need to be efficient since it should only +		 * happen when the power fails during a write, then only one +		 * chunk should ever be affected. +		 * +		 * Correction for YAFFS2: This could happen quite a lot and we +		 * need to think about efficiency! TODO +		 * Update: For backward scanning we don't need to re-read tags +		 * so this is quite cheap.  		 */ -		while (fStruct->topLevel && !done) { -			tn = fStruct->top; +		if (existing_cunk > 0) { +			/* NB Right now existing chunk will not be real +			 * chunk_id if the chunk group size > 1 +			 * thus we have to do a FindChunkInFile to get the +			 * real chunk id. +			 * +			 * We have a duplicate now we need to decide which +			 * one to use: +			 * +			 * Backwards scanning YAFFS2: The old one is what +			 * we use, dump the new one. +			 * YAFFS1: Get both sets of tags and compare serial +			 * numbers. +			 */ -			hasData = 0; -			for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { -				if (tn->internal[i]) { -					hasData++; -				} -			} +			if (in_scan > 0) { +				/* Only do this for forward scanning */ +				yaffs_rd_chunk_tags_nand(dev, +							 nand_chunk, +							 NULL, &new_tags); -			if (!hasData) { -				fStruct->top = tn->internal[0]; -				fStruct->topLevel--; -				yaffs_FreeTnode(dev, tn); -			} else { -				done = 1; +				/* Do a proper find */ +				existing_cunk = +				    yaffs_find_chunk_in_file(in, inode_chunk, +							     &existing_tags);  			} -		} -	} -	return YAFFS_OK; -} +			if (existing_cunk <= 0) { +				/*Hoosterman - how did this happen? */ -/*-------------------- End of File Structure functions.-------------------*/ +				yaffs_trace(YAFFS_TRACE_ERROR, +					"yaffs tragedy: existing chunk < 0 in scan" +					); -/* yaffs_CreateFreeObjects creates a bunch more objects and - * adds them to the object free list. - */ -static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects) -{ -	int i; -	yaffs_Object *newObjects; -	yaffs_ObjectList *list; +			} -	if (nObjects < 1) -		return YAFFS_OK; +			/* NB The deleted flags should be false, otherwise +			 * the chunks will not be loaded during a scan +			 */ -	/* make these things */ -	newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); -	list = YMALLOC(sizeof(yaffs_ObjectList)); +			if (in_scan > 0) { +				new_serial = new_tags.serial_number; +				existing_serial = existing_tags.serial_number; +			} -	if (!newObjects || !list) { -		if(newObjects) -			YFREE(newObjects); -		if(list) -			YFREE(list); -		T(YAFFS_TRACE_ALLOCATE, -		  (TSTR("yaffs: Could not allocate more objects" TENDSTR))); -		return YAFFS_FAIL; -	} +			if ((in_scan > 0) && +			    (existing_cunk <= 0 || +			     ((existing_serial + 1) & 3) == new_serial)) { +				/* Forward scanning. +				 * Use new +				 * Delete the old one and drop through to +				 * update the tnode +				 */ +				yaffs_chunk_del(dev, existing_cunk, 1, +						__LINE__); +			} else { +				/* Backward scanning or we want to use the +				 * existing one +				 * Delete the new one and return early so that +				 * the tnode isn't changed +				 */ +				yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); +				return YAFFS_OK; +			} +		} -	/* Hook them into the free list */ -	for (i = 0; i < nObjects - 1; i++) { -		newObjects[i].siblings.next = -		    (struct list_head *)(&newObjects[i + 1]);  	} -	newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects; -	dev->freeObjects = newObjects; -	dev->nFreeObjects += nObjects; -	dev->nObjectsCreated += nObjects; - -	/* Now add this bunch of Objects to a list for freeing up. */ +	if (existing_cunk == 0) +		in->n_data_chunks++; -	list->objects = newObjects; -	list->next = dev->allocatedObjectList; -	dev->allocatedObjectList = list; +	yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk);  	return YAFFS_OK;  } - -/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */ -static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device * dev) +static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk)  { -	yaffs_Object *tn = NULL; +	struct yaffs_block_info *the_block; +	unsigned block_no; + +	yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); -	/* If there are none left make more */ -	if (!dev->freeObjects) { -		yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS); +	block_no = chunk / dev->param.chunks_per_block; +	the_block = yaffs_get_block_info(dev, block_no); +	if (the_block) { +		the_block->soft_del_pages++; +		dev->n_free_chunks++; +		yaffs2_update_oldest_dirty_seq(dev, block_no, the_block);  	} +} -	if (dev->freeObjects) { -		tn = dev->freeObjects; -		dev->freeObjects = -		    (yaffs_Object *) (dev->freeObjects->siblings.next); -		dev->nFreeObjects--; +/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all + * the chunks in the file. + * All soft deleting does is increment the block's softdelete count and pulls + * the chunk out of the tnode. + * Thus, essentially this is the same as DeleteWorker except that the chunks + * are soft deleted. + */ -		/* Now sweeten it up... */ +static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, +				 u32 level, int chunk_offset) +{ +	int i; +	int the_chunk; +	int all_done = 1; +	struct yaffs_dev *dev = in->my_dev; -		memset(tn, 0, sizeof(yaffs_Object)); -		tn->myDev = dev; -		tn->chunkId = -1; -		tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; -		INIT_LIST_HEAD(&(tn->hardLinks)); -		INIT_LIST_HEAD(&(tn->hashLink)); -		INIT_LIST_HEAD(&tn->siblings); +	if (!tn) +		return 1; -		/* Add it to the lost and found directory. -		 * NB Can't put root or lostNFound in lostNFound so -		 * check if lostNFound exists first -		 */ -		if (dev->lostNFoundDir) { -			yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn); +	if (level > 0) { +		for (i = YAFFS_NTNODES_INTERNAL - 1; +			all_done && i >= 0; +			i--) { +			if (tn->internal[i]) { +				all_done = +				    yaffs_soft_del_worker(in, +					tn->internal[i], +					level - 1, +					(chunk_offset << +					YAFFS_TNODES_INTERNAL_BITS) +					+ i); +				if (all_done) { +					yaffs_free_tnode(dev, +						tn->internal[i]); +					tn->internal[i] = NULL; +				} else { +					/* Can this happen? */ +				} +			}  		} +		return (all_done) ? 1 : 0;  	} -	return tn; +	/* level 0 */ +	 for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { +		the_chunk = yaffs_get_group_base(dev, tn, i); +		if (the_chunk) { +			yaffs_soft_del_chunk(dev, the_chunk); +			yaffs_load_tnode_0(dev, tn, i, 0); +		} +	} +	return 1;  } -static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device * dev, int number, -					       __u32 mode) +static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj)  { +	struct yaffs_dev *dev = obj->my_dev; +	struct yaffs_obj *parent; -	yaffs_Object *obj = -	    yaffs_CreateNewObject(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); -	if (obj) { -		obj->fake = 1;		/* it is fake so it has no NAND presence... */ -		obj->renameAllowed = 0;	/* ... and we're not allowed to rename it... */ -		obj->unlinkAllowed = 0;	/* ... or unlink it */ -		obj->deleted = 0; -		obj->unlinked = 0; -		obj->yst_mode = mode; -		obj->myDev = dev; -		obj->chunkId = 0;	/* Not a valid chunk. */ -	} +	yaffs_verify_obj_in_dir(obj); +	parent = obj->parent; -	return obj; +	yaffs_verify_dir(parent); -} - -static void yaffs_UnhashObject(yaffs_Object * tn) -{ -	int bucket; -	yaffs_Device *dev = tn->myDev; +	if (dev && dev->param.remove_obj_fn) +		dev->param.remove_obj_fn(obj); -	/* If it is still linked into the bucket list, free from the list */ -	if (!list_empty(&tn->hashLink)) { -		list_del_init(&tn->hashLink); -		bucket = yaffs_HashFunction(tn->objectId); -		dev->objectBucket[bucket].count--; -	} +	list_del_init(&obj->siblings); +	obj->parent = NULL; +	yaffs_verify_dir(parent);  } -/*  FreeObject frees up a Object and puts it back on the free list */ -static void yaffs_FreeObject(yaffs_Object * tn) +void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj)  { - -	yaffs_Device *dev = tn->myDev; - -/* XXX U-BOOT XXX */ -#if 0 -#ifdef  __KERNEL__ -	if (tn->myInode) { -		/* We're still hooked up to a cached inode. -		 * Don't delete now, but mark for later deletion -		 */ -		tn->deferedFree = 1; +	if (!directory) { +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"tragedy: Trying to add an object to a null pointer directory" +			); +		BUG();  		return;  	} -#endif -#endif -	yaffs_UnhashObject(tn); - -	/* Link into the free list. */ -	tn->siblings.next = (struct list_head *)(dev->freeObjects); -	dev->freeObjects = tn; -	dev->nFreeObjects++; -} - -/* XXX U-BOOT XXX */ -#if 0 -#ifdef __KERNEL__ - -void yaffs_HandleDeferedFree(yaffs_Object * obj) -{ -	if (obj->deferedFree) { -		yaffs_FreeObject(obj); +	if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"tragedy: Trying to add an object to a non-directory" +			); +		BUG();  	} -} -#endif -#endif +	if (obj->siblings.prev == NULL) { +		/* Not initialised */ +		BUG(); +	} -static void yaffs_DeinitialiseObjects(yaffs_Device * dev) -{ -	/* Free the list of allocated Objects */ +	yaffs_verify_dir(directory); -	yaffs_ObjectList *tmp; +	yaffs_remove_obj_from_dir(obj); -	while (dev->allocatedObjectList) { -		tmp = dev->allocatedObjectList->next; -		YFREE(dev->allocatedObjectList->objects); -		YFREE(dev->allocatedObjectList); +	/* Now add it */ +	list_add(&obj->siblings, &directory->variant.dir_variant.children); +	obj->parent = directory; -		dev->allocatedObjectList = tmp; +	if (directory == obj->my_dev->unlinked_dir +	    || directory == obj->my_dev->del_dir) { +		obj->unlinked = 1; +		obj->my_dev->n_unlinked_files++; +		obj->rename_allowed = 0;  	} -	dev->freeObjects = NULL; -	dev->nFreeObjects = 0; +	yaffs_verify_dir(directory); +	yaffs_verify_obj_in_dir(obj);  } -static void yaffs_InitialiseObjects(yaffs_Device * dev) +static int yaffs_change_obj_name(struct yaffs_obj *obj, +				 struct yaffs_obj *new_dir, +				 const YCHAR *new_name, int force, int shadows)  { -	int i; +	int unlink_op; +	int del_op; +	struct yaffs_obj *existing_target; -	dev->allocatedObjectList = NULL; -	dev->freeObjects = NULL; -	dev->nFreeObjects = 0; +	if (new_dir == NULL) +		new_dir = obj->parent;	/* use the old directory */ -	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -		INIT_LIST_HEAD(&dev->objectBucket[i].list); -		dev->objectBucket[i].count = 0; +	if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"tragedy: yaffs_change_obj_name: new_dir is not a directory" +			); +		BUG();  	} -} +	unlink_op = (new_dir == obj->my_dev->unlinked_dir); +	del_op = (new_dir == obj->my_dev->del_dir); -static int yaffs_FindNiceObjectBucket(yaffs_Device * dev) -{ -	static int x = 0; -	int i; -	int l = 999; -	int lowest = 999999; +	existing_target = yaffs_find_by_name(new_dir, new_name); -	/* First let's see if we can find one that's empty. */ - -	for (i = 0; i < 10 && lowest > 0; i++) { -		x++; -		x %= YAFFS_NOBJECT_BUCKETS; -		if (dev->objectBucket[x].count < lowest) { -			lowest = dev->objectBucket[x].count; -			l = x; -		} - -	} - -	/* If we didn't find an empty list, then try -	 * looking a bit further for a short one +	/* If the object is a file going into the unlinked directory, +	 *   then it is OK to just stuff it in since duplicate names are OK. +	 *   else only proceed if the new name does not exist and we're putting +	 *   it into a directory.  	 */ +	if (!(unlink_op || del_op || force || +	      shadows > 0 || !existing_target) || +	      new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) +		return YAFFS_FAIL; -	for (i = 0; i < 10 && lowest > 3; i++) { -		x++; -		x %= YAFFS_NOBJECT_BUCKETS; -		if (dev->objectBucket[x].count < lowest) { -			lowest = dev->objectBucket[x].count; -			l = x; -		} +	yaffs_set_obj_name(obj, new_name); +	obj->dirty = 1; +	yaffs_add_obj_to_dir(new_dir, obj); -	} +	if (unlink_op) +		obj->unlinked = 1; -	return l; +	/* If it is a deletion then we mark it as a shrink for gc  */ +	if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) +		return YAFFS_OK; + +	return YAFFS_FAIL;  } -static int yaffs_CreateNewObjectNumber(yaffs_Device * dev) +/*------------------------ Short Operations Cache ------------------------------ + *   In many situations where there is no high level buffering  a lot of + *   reads might be short sequential reads, and a lot of writes may be short + *   sequential writes. eg. scanning/writing a jpeg file. + *   In these cases, a short read/write cache can provide a huge perfomance + *   benefit with dumb-as-a-rock code. + *   In Linux, the page cache provides read buffering and the short op cache + *   provides write buffering. + * + *   There are a small number (~10) of cache chunks per device so that we don't + *   need a very intelligent search. + */ + +static int yaffs_obj_cache_dirty(struct yaffs_obj *obj)  { -	int bucket = yaffs_FindNiceObjectBucket(dev); +	struct yaffs_dev *dev = obj->my_dev; +	int i; +	struct yaffs_cache *cache; +	int n_caches = obj->my_dev->param.n_caches; -	/* Now find an object value that has not already been taken -	 * by scanning the list. -	 */ +	for (i = 0; i < n_caches; i++) { +		cache = &dev->cache[i]; +		if (cache->object == obj && cache->dirty) +			return 1; +	} -	int found = 0; -	struct list_head *i; +	return 0; +} -	__u32 n = (__u32) bucket; +static void yaffs_flush_file_cache(struct yaffs_obj *obj) +{ +	struct yaffs_dev *dev = obj->my_dev; +	int lowest = -99;	/* Stop compiler whining. */ +	int i; +	struct yaffs_cache *cache; +	int chunk_written = 0; +	int n_caches = obj->my_dev->param.n_caches; -	/* yaffs_CheckObjectHashSanity();  */ +	if (n_caches < 1) +		return; +	do { +		cache = NULL; -	while (!found) { -		found = 1; -		n += YAFFS_NOBJECT_BUCKETS; -		if (1 || dev->objectBucket[bucket].count > 0) { -			list_for_each(i, &dev->objectBucket[bucket].list) { -				/* If there is already one in the list */ -				if (i -				    && list_entry(i, yaffs_Object, -						  hashLink)->objectId == n) { -					found = 0; +		/* Find the lowest dirty chunk for this object */ +		for (i = 0; i < n_caches; i++) { +			if (dev->cache[i].object == obj && +			    dev->cache[i].dirty) { +				if (!cache || +				    dev->cache[i].chunk_id < lowest) { +					cache = &dev->cache[i]; +					lowest = cache->chunk_id;  				}  			}  		} -	} +		if (cache && !cache->locked) { +			/* Write it out and free it up */ +			chunk_written = +			    yaffs_wr_data_obj(cache->object, +					      cache->chunk_id, +					      cache->data, +					      cache->n_bytes, 1); +			cache->dirty = 0; +			cache->object = NULL; +		} +	} while (cache && chunk_written > 0); -	return n; +	if (cache) +		/* Hoosterman, disk full while writing cache out. */ +		yaffs_trace(YAFFS_TRACE_ERROR, +			"yaffs tragedy: no space during cache write");  } -static void yaffs_HashObject(yaffs_Object * in) +/*yaffs_flush_whole_cache(dev) + * + * + */ + +void yaffs_flush_whole_cache(struct yaffs_dev *dev)  { -	int bucket = yaffs_HashFunction(in->objectId); -	yaffs_Device *dev = in->myDev; +	struct yaffs_obj *obj; +	int n_caches = dev->param.n_caches; +	int i; -	list_add(&in->hashLink, &dev->objectBucket[bucket].list); -	dev->objectBucket[bucket].count++; +	/* Find a dirty object in the cache and flush it... +	 * until there are no further dirty objects. +	 */ +	do { +		obj = NULL; +		for (i = 0; i < n_caches && !obj; i++) { +			if (dev->cache[i].object && dev->cache[i].dirty) +				obj = dev->cache[i].object; +		} +		if (obj) +			yaffs_flush_file_cache(obj); +	} while (obj);  } -yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number) +/* Grab us a cache chunk for use. + * First look for an empty one. + * Then look for the least recently used non-dirty one. + * Then look for the least recently used dirty one...., flush and look again. + */ +static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev)  { -	int bucket = yaffs_HashFunction(number); -	struct list_head *i; -	yaffs_Object *in; +	int i; -	list_for_each(i, &dev->objectBucket[bucket].list) { -		/* Look if it is in the list */ -		if (i) { -			in = list_entry(i, yaffs_Object, hashLink); -			if (in->objectId == number) { -/* XXX U-BOOT XXX */ -#if 0 -#ifdef __KERNEL__ -				/* Don't tell the VFS about this one if it is defered free */ -				if (in->deferedFree) -					return NULL; -#endif -#endif -				return in; -			} +	if (dev->param.n_caches > 0) { +		for (i = 0; i < dev->param.n_caches; i++) { +			if (!dev->cache[i].object) +				return &dev->cache[i];  		}  	} -  	return NULL;  } -yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, -				    yaffs_ObjectType type) +static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev)  { +	struct yaffs_cache *cache; +	struct yaffs_obj *the_obj; +	int usage; +	int i; +	int pushout; -	yaffs_Object *theObject; -	yaffs_Tnode *tn = NULL; - -	if (number < 0) { -		number = yaffs_CreateNewObjectNumber(dev); -	} - -	theObject = yaffs_AllocateEmptyObject(dev); -	if(!theObject) +	if (dev->param.n_caches < 1)  		return NULL; -	if(type == YAFFS_OBJECT_TYPE_FILE){ -		tn = yaffs_GetTnode(dev); -		if(!tn){ -			yaffs_FreeObject(theObject); -			return NULL; -		} -	} +	/* Try find a non-dirty one... */ +	cache = yaffs_grab_chunk_worker(dev); +	if (!cache) { +		/* They were all dirty, find the LRU object and flush +		 * its cache, then  find again. +		 * NB what's here is not very accurate, +		 * we actually flush the object with the LRU chunk. +		 */ -	if (theObject) { -		theObject->fake = 0; -		theObject->renameAllowed = 1; -		theObject->unlinkAllowed = 1; -		theObject->objectId = number; -		yaffs_HashObject(theObject); -		theObject->variantType = type; -#ifdef CONFIG_YAFFS_WINCE -		yfsd_WinFileTimeNow(theObject->win_atime); -		theObject->win_ctime[0] = theObject->win_mtime[0] = -		    theObject->win_atime[0]; -		theObject->win_ctime[1] = theObject->win_mtime[1] = -		    theObject->win_atime[1]; +		/* With locking we can't assume we can use entry zero, +		 * Set the_obj to a valid pointer for Coverity. */ +		the_obj = dev->cache[0].object; +		usage = -1; +		cache = NULL; +		pushout = -1; -#else +		for (i = 0; i < dev->param.n_caches; i++) { +			if (dev->cache[i].object && +			    !dev->cache[i].locked && +			    (dev->cache[i].last_use < usage || +			    !cache)) { +				usage = dev->cache[i].last_use; +				the_obj = dev->cache[i].object; +				cache = &dev->cache[i]; +				pushout = i; +			} +		} -		theObject->yst_atime = theObject->yst_mtime = -		    theObject->yst_ctime = Y_CURRENT_TIME; -#endif -		switch (type) { -		case YAFFS_OBJECT_TYPE_FILE: -			theObject->variant.fileVariant.fileSize = 0; -			theObject->variant.fileVariant.scannedFileSize = 0; -			theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF;	/* max __u32 */ -			theObject->variant.fileVariant.topLevel = 0; -			theObject->variant.fileVariant.top = tn; -			break; -		case YAFFS_OBJECT_TYPE_DIRECTORY: -			INIT_LIST_HEAD(&theObject->variant.directoryVariant. -				       children); -			break; -		case YAFFS_OBJECT_TYPE_SYMLINK: -		case YAFFS_OBJECT_TYPE_HARDLINK: -		case YAFFS_OBJECT_TYPE_SPECIAL: -			/* No action required */ -			break; -		case YAFFS_OBJECT_TYPE_UNKNOWN: -			/* todo this should not happen */ -			break; +		if (!cache || cache->dirty) { +			/* Flush and try again */ +			yaffs_flush_file_cache(the_obj); +			cache = yaffs_grab_chunk_worker(dev);  		}  	} - -	return theObject; +	return cache;  } -static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device * dev, -						      int number, -						      yaffs_ObjectType type) +/* Find a cached chunk */ +static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, +						  int chunk_id)  { -	yaffs_Object *theObject = NULL; - -	if (number > 0) { -		theObject = yaffs_FindObjectByNumber(dev, number); -	} +	struct yaffs_dev *dev = obj->my_dev; +	int i; -	if (!theObject) { -		theObject = yaffs_CreateNewObject(dev, number, type); -	} +	if (dev->param.n_caches < 1) +		return NULL; -	return theObject; +	for (i = 0; i < dev->param.n_caches; i++) { +		if (dev->cache[i].object == obj && +		    dev->cache[i].chunk_id == chunk_id) { +			dev->cache_hits++; +			return &dev->cache[i]; +		} +	} +	return NULL;  } - -static YCHAR *yaffs_CloneString(const YCHAR * str) +/* Mark the chunk for the least recently used algorithym */ +static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, +			    int is_write)  { -	YCHAR *newStr = NULL; +	int i; -	if (str && *str) { -		newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR)); -		if(newStr) -			yaffs_strcpy(newStr, str); -	} +	if (dev->param.n_caches < 1) +		return; + +	if (dev->cache_last_use < 0 || +		dev->cache_last_use > 100000000) { +		/* Reset the cache usages */ +		for (i = 1; i < dev->param.n_caches; i++) +			dev->cache[i].last_use = 0; -	return newStr; +		dev->cache_last_use = 0; +	} +	dev->cache_last_use++; +	cache->last_use = dev->cache_last_use; +	if (is_write) +		cache->dirty = 1;  } -/* - * Mknod (create) a new object. - * equivalentObject only has meaning for a hard link; - * aliasString only has meaning for a sumlink. - * rdev only has meaning for devices (a subset of special objects) +/* Invalidate a single cache page. + * Do this when a whole page gets written, + * ie the short cache for this page is no longer valid.   */ - -static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, -				       yaffs_Object * parent, -				       const YCHAR * name, -				       __u32 mode, -				       __u32 uid, -				       __u32 gid, -				       yaffs_Object * equivalentObject, -				       const YCHAR * aliasString, __u32 rdev) +static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id)  { -	yaffs_Object *in; -	YCHAR *str = NULL; +	struct yaffs_cache *cache; -	yaffs_Device *dev = parent->myDev; +	if (object->my_dev->param.n_caches > 0) { +		cache = yaffs_find_chunk_cache(object, chunk_id); -	/* Check if the entry exists. If it does then fail the call since we don't want a dup.*/ -	if (yaffs_FindObjectByName(parent, name)) { -		return NULL; +		if (cache) +			cache->object = NULL;  	} +} -	in = yaffs_CreateNewObject(dev, -1, type); +/* Invalidate all the cache pages associated with this object + * Do this whenever ther file is deleted or resized. + */ +static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) +{ +	int i; +	struct yaffs_dev *dev = in->my_dev; -	if(type == YAFFS_OBJECT_TYPE_SYMLINK){ -		str = yaffs_CloneString(aliasString); -		if(!str){ -			yaffs_FreeObject(in); -			return NULL; +	if (dev->param.n_caches > 0) { +		/* Invalidate it. */ +		for (i = 0; i < dev->param.n_caches; i++) { +			if (dev->cache[i].object == in) +				dev->cache[i].object = NULL;  		}  	} +} +static void yaffs_unhash_obj(struct yaffs_obj *obj) +{ +	int bucket; +	struct yaffs_dev *dev = obj->my_dev; +	/* If it is still linked into the bucket list, free from the list */ +	if (!list_empty(&obj->hash_link)) { +		list_del_init(&obj->hash_link); +		bucket = yaffs_hash_fn(obj->obj_id); +		dev->obj_bucket[bucket].count--; +	} +} -	if (in) { -		in->chunkId = -1; -		in->valid = 1; -		in->variantType = type; - -		in->yst_mode = mode; - -#ifdef CONFIG_YAFFS_WINCE -		yfsd_WinFileTimeNow(in->win_atime); -		in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0]; -		in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1]; - -#else -		in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME; - -		in->yst_rdev = rdev; -		in->yst_uid = uid; -		in->yst_gid = gid; -#endif -		in->nDataChunks = 0; - -		yaffs_SetObjectName(in, name); -		in->dirty = 1; - -		yaffs_AddObjectToDirectory(parent, in); - -		in->myDev = parent->myDev; - -		switch (type) { -		case YAFFS_OBJECT_TYPE_SYMLINK: -			in->variant.symLinkVariant.alias = str; -			break; -		case YAFFS_OBJECT_TYPE_HARDLINK: -			in->variant.hardLinkVariant.equivalentObject = -			    equivalentObject; -			in->variant.hardLinkVariant.equivalentObjectId = -			    equivalentObject->objectId; -			list_add(&in->hardLinks, &equivalentObject->hardLinks); -			break; -		case YAFFS_OBJECT_TYPE_FILE: -		case YAFFS_OBJECT_TYPE_DIRECTORY: -		case YAFFS_OBJECT_TYPE_SPECIAL: -		case YAFFS_OBJECT_TYPE_UNKNOWN: -			/* do nothing */ -			break; -		} +/*  FreeObject frees up a Object and puts it back on the free list */ +static void yaffs_free_obj(struct yaffs_obj *obj) +{ +	struct yaffs_dev *dev; -		if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) { -			/* Could not create the object header, fail the creation */ -			yaffs_DestroyObject(in); -			in = NULL; -		} +	if (!obj) { +		BUG(); +		return; +	} +	dev = obj->my_dev; +	yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", +		obj, obj->my_inode); +	if (obj->parent) +		BUG(); +	if (!list_empty(&obj->siblings)) +		BUG(); +	if (obj->my_inode) { +		/* We're still hooked up to a cached inode. +		 * Don't delete now, but mark for later deletion +		 */ +		obj->defered_free = 1; +		return;  	} -	return in; -} +	yaffs_unhash_obj(obj); -yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name, -			      __u32 mode, __u32 uid, __u32 gid) -{ -	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, -				 uid, gid, NULL, NULL, 0); +	yaffs_free_raw_obj(dev, obj); +	dev->n_obj--; +	dev->checkpoint_blocks_required = 0;	/* force recalculation */  } -yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name, -				   __u32 mode, __u32 uid, __u32 gid) +void yaffs_handle_defered_free(struct yaffs_obj *obj)  { -	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, -				 mode, uid, gid, NULL, NULL, 0); +	if (obj->defered_free) +		yaffs_free_obj(obj);  } -yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name, -				 __u32 mode, __u32 uid, __u32 gid, __u32 rdev) +static int yaffs_generic_obj_del(struct yaffs_obj *in)  { -	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, -				 uid, gid, NULL, NULL, rdev); -} +	/* Iinvalidate the file's data in the cache, without flushing. */ +	yaffs_invalidate_whole_cache(in); -yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name, -				 __u32 mode, __u32 uid, __u32 gid, -				 const YCHAR * alias) -{ -	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, -				 uid, gid, NULL, alias, 0); -} +	if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { +		/* Move to unlinked directory so we have a deletion record */ +		yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, +				      0); +	} -/* yaffs_Link returns the object id of the equivalent object.*/ -yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name, -			 yaffs_Object * equivalentObject) -{ -	/* Get the real object in case we were fed a hard link as an equivalent object */ -	equivalentObject = yaffs_GetEquivalentObject(equivalentObject); +	yaffs_remove_obj_from_dir(in); +	yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); +	in->hdr_chunk = 0; -	if (yaffs_MknodObject -	    (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0, -	     equivalentObject, NULL, 0)) { -		return equivalentObject; -	} else { -		return NULL; -	} +	yaffs_free_obj(in); +	return YAFFS_OK;  } -static int yaffs_ChangeObjectName(yaffs_Object * obj, yaffs_Object * newDir, -				  const YCHAR * newName, int force, int shadows) +static void yaffs_soft_del_file(struct yaffs_obj *obj)  { -	int unlinkOp; -	int deleteOp; - -	yaffs_Object *existingTarget; - -	if (newDir == NULL) { -		newDir = obj->parent;	/* use the old directory */ -	} - -	if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("tragendy: yaffs_ChangeObjectName: newDir is not a directory" -		    TENDSTR))); -		YBUG(); -	} +	if (!obj->deleted || +	    obj->variant_type != YAFFS_OBJECT_TYPE_FILE || +	    obj->soft_del) +		return; -	/* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */ -	if (obj->myDev->isYaffs2) { -		unlinkOp = (newDir == obj->myDev->unlinkedDir); +	if (obj->n_data_chunks <= 0) { +		/* Empty file with no duplicate object headers, +		 * just delete it immediately */ +		yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); +		obj->variant.file_variant.top = NULL; +		yaffs_trace(YAFFS_TRACE_TRACING, +			"yaffs: Deleting empty file %d", +			obj->obj_id); +		yaffs_generic_obj_del(obj);  	} else { -		unlinkOp = (newDir == obj->myDev->unlinkedDir -			    && obj->variantType == YAFFS_OBJECT_TYPE_FILE); +		yaffs_soft_del_worker(obj, +				      obj->variant.file_variant.top, +				      obj->variant. +				      file_variant.top_level, 0); +		obj->soft_del = 1;  	} +} -	deleteOp = (newDir == obj->myDev->deletedDir); +/* Pruning removes any part of the file structure tree that is beyond the + * bounds of the file (ie that does not point to chunks). + * + * A file should only get pruned when its size is reduced. + * + * Before pruning, the chunks must be pulled from the tree and the + * level 0 tnode entries must be zeroed out. + * Could also use this for file deletion, but that's probably better handled + * by a special case. + * + * This function is recursive. For levels > 0 the function is called again on + * any sub-tree. For level == 0 we just check if the sub-tree has data. + * If there is no data in a subtree then it is pruned. + */ -	existingTarget = yaffs_FindObjectByName(newDir, newName); +static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, +					      struct yaffs_tnode *tn, u32 level, +					      int del0) +{ +	int i; +	int has_data; -	/* If the object is a file going into the unlinked directory, -	 *   then it is OK to just stuff it in since duplicate names are allowed. -	 *   else only proceed if the new name does not exist and if we're putting -	 *   it into a directory. -	 */ -	if ((unlinkOp || -	     deleteOp || -	     force || -	     (shadows > 0) || -	     !existingTarget) && -	    newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) { -		yaffs_SetObjectName(obj, newName); -		obj->dirty = 1; +	if (!tn) +		return tn; -		yaffs_AddObjectToDirectory(newDir, obj); +	has_data = 0; -		if (unlinkOp) -			obj->unlinked = 1; +	if (level > 0) { +		for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { +			if (tn->internal[i]) { +				tn->internal[i] = +				    yaffs_prune_worker(dev, +						tn->internal[i], +						level - 1, +						(i == 0) ? del0 : 1); +			} -		/* If it is a deletion then we mark it as a shrink for gc purposes. */ -		if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows)>= 0) -			return YAFFS_OK; +			if (tn->internal[i]) +				has_data++; +		} +	} else { +		int tnode_size_u32 = dev->tnode_size / sizeof(u32); +		u32 *map = (u32 *) tn; + +		for (i = 0; !has_data && i < tnode_size_u32; i++) { +			if (map[i]) +				has_data++; +		}  	} -	return YAFFS_FAIL; +	if (has_data == 0 && del0) { +		/* Free and return NULL */ +		yaffs_free_tnode(dev, tn); +		tn = NULL; +	} +	return tn;  } -int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName, -		       yaffs_Object * newDir, const YCHAR * newName) +static int yaffs_prune_tree(struct yaffs_dev *dev, +			    struct yaffs_file_var *file_struct)  { -	yaffs_Object *obj; -	yaffs_Object *existingTarget; -	int force = 0; +	int i; +	int has_data; +	int done = 0; +	struct yaffs_tnode *tn; -#ifdef CONFIG_YAFFS_CASE_INSENSITIVE -	/* Special case for case insemsitive systems (eg. WinCE). -	 * While look-up is case insensitive, the name isn't. -	 * Therefore we might want to change x.txt to X.txt -	*/ -	if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0) { -		force = 1; -	} -#endif +	if (file_struct->top_level < 1) +		return YAFFS_OK; -	obj = yaffs_FindObjectByName(oldDir, oldName); -	/* Check new name to long. */ -	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK && -	    yaffs_strlen(newName) > YAFFS_MAX_ALIAS_LENGTH) -	  /* ENAMETOOLONG */ -	  return YAFFS_FAIL; -	else if (obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK && -		 yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH) -	  /* ENAMETOOLONG */ -	  return YAFFS_FAIL; +	file_struct->top = +	   yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); -	if (obj && obj->renameAllowed) { +	/* Now we have a tree with all the non-zero branches NULL but +	 * the height is the same as it was. +	 * Let's see if we can trim internal tnodes to shorten the tree. +	 * We can do this if only the 0th element in the tnode is in use +	 * (ie all the non-zero are NULL) +	 */ -		/* Now do the handling for an existing target, if there is one */ +	while (file_struct->top_level && !done) { +		tn = file_struct->top; -		existingTarget = yaffs_FindObjectByName(newDir, newName); -		if (existingTarget && -		    existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && -		    !list_empty(&existingTarget->variant.directoryVariant.children)) { -			/* There is a target that is a non-empty directory, so we fail */ -			return YAFFS_FAIL;	/* EEXIST or ENOTEMPTY */ -		} else if (existingTarget && existingTarget != obj) { -			/* Nuke the target first, using shadowing, -			 * but only if it isn't the same object -			 */ -			yaffs_ChangeObjectName(obj, newDir, newName, force, -					       existingTarget->objectId); -			yaffs_UnlinkObject(existingTarget); +		has_data = 0; +		for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { +			if (tn->internal[i]) +				has_data++;  		} -		return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); +		if (!has_data) { +			file_struct->top = tn->internal[0]; +			file_struct->top_level--; +			yaffs_free_tnode(dev, tn); +		} else { +			done = 1; +		}  	} -	return YAFFS_FAIL; + +	return YAFFS_OK;  } -/*------------------------- Block Management and Page Allocation ----------------*/ +/*-------------------- End of File Structure functions.-------------------*/ -static int yaffs_InitialiseBlocks(yaffs_Device * dev) +/* alloc_empty_obj gets us a clean Object.*/ +static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev)  { -	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; +	struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); -	dev->blockInfo = NULL; -	dev->chunkBits = NULL; +	if (!obj) +		return obj; -	dev->allocationBlock = -1;	/* force it to get a new one */ +	dev->n_obj++; -	/* If the first allocation strategy fails, thry the alternate one */ -	dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo)); -	if(!dev->blockInfo){ -		dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo)); -		dev->blockInfoAlt = 1; -	} -	else -		dev->blockInfoAlt = 0; +	/* Now sweeten it up... */ -	if(dev->blockInfo){ +	memset(obj, 0, sizeof(struct yaffs_obj)); +	obj->being_created = 1; -		/* Set up dynamic blockinfo stuff. */ -		dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; /* round up bytes */ -		dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks); -		if(!dev->chunkBits){ -			dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks); -			dev->chunkBitsAlt = 1; -		} -		else -			dev->chunkBitsAlt = 0; -	} +	obj->my_dev = dev; +	obj->hdr_chunk = 0; +	obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; +	INIT_LIST_HEAD(&(obj->hard_links)); +	INIT_LIST_HEAD(&(obj->hash_link)); +	INIT_LIST_HEAD(&obj->siblings); -	if (dev->blockInfo && dev->chunkBits) { -		memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo)); -		memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks); -		return YAFFS_OK; +	/* Now make the directory sane */ +	if (dev->root_dir) { +		obj->parent = dev->root_dir; +		list_add(&(obj->siblings), +			 &dev->root_dir->variant.dir_variant.children);  	} -	return YAFFS_FAIL; - -} - -static void yaffs_DeinitialiseBlocks(yaffs_Device * dev) -{ -	if(dev->blockInfoAlt && dev->blockInfo) -		YFREE_ALT(dev->blockInfo); -	else if(dev->blockInfo) -		YFREE(dev->blockInfo); +	/* Add it to the lost and found directory. +	 * NB Can't put root or lost-n-found in lost-n-found so +	 * check if lost-n-found exists first +	 */ +	if (dev->lost_n_found) +		yaffs_add_obj_to_dir(dev->lost_n_found, obj); -	dev->blockInfoAlt = 0; +	obj->being_created = 0; -	dev->blockInfo = NULL; +	dev->checkpoint_blocks_required = 0;	/* force recalculation */ -	if(dev->chunkBitsAlt && dev->chunkBits) -		YFREE_ALT(dev->chunkBits); -	else if(dev->chunkBits) -		YFREE(dev->chunkBits); -	dev->chunkBitsAlt = 0; -	dev->chunkBits = NULL; +	return obj;  } -static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev, -					    yaffs_BlockInfo * bi) +static int yaffs_find_nice_bucket(struct yaffs_dev *dev)  {  	int i; -	__u32 seq; -	yaffs_BlockInfo *b; - -	if (!dev->isYaffs2) -		return 1;	/* disqualification only applies to yaffs2. */ - -	if (!bi->hasShrinkHeader) -		return 1;	/* can gc */ +	int l = 999; +	int lowest = 999999; -	/* Find the oldest dirty sequence number if we don't know it and save it -	 * so we don't have to keep recomputing it. +	/* Search for the shortest list or one that +	 * isn't too long.  	 */ -	if (!dev->oldestDirtySequence) { -		seq = dev->sequenceNumber; -		for (i = dev->internalStartBlock; i <= dev->internalEndBlock; -		     i++) { -			b = yaffs_GetBlockInfo(dev, i); -			if (b->blockState == YAFFS_BLOCK_STATE_FULL && -			    (b->pagesInUse - b->softDeletions) < -			    dev->nChunksPerBlock && b->sequenceNumber < seq) { -				seq = b->sequenceNumber; -			} +	for (i = 0; i < 10 && lowest > 4; i++) { +		dev->bucket_finder++; +		dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; +		if (dev->obj_bucket[dev->bucket_finder].count < lowest) { +			lowest = dev->obj_bucket[dev->bucket_finder].count; +			l = dev->bucket_finder;  		} -		dev->oldestDirtySequence = seq;  	} -	/* Can't do gc of this block if there are any blocks older than this one that have -	 * discarded pages. -	 */ -	return (bi->sequenceNumber <= dev->oldestDirtySequence); - +	return l;  } -/* FindDiretiestBlock is used to select the dirtiest block (or close enough) - * for garbage collection. - */ - -static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, -					       int aggressive) +static int yaffs_new_obj_id(struct yaffs_dev *dev)  { +	int bucket = yaffs_find_nice_bucket(dev); +	int found = 0; +	struct list_head *i; +	u32 n = (u32) bucket; -	int b = dev->currentDirtyChecker; - -	int i; -	int iterations; -	int dirtiest = -1; -	int pagesInUse = 0; -	int prioritised=0; -	yaffs_BlockInfo *bi; -	int pendingPrioritisedExist = 0; - -	/* First let's see if we need to grab a prioritised block */ -	if(dev->hasPendingPrioritisedGCs){ -		for(i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++){ - -			bi = yaffs_GetBlockInfo(dev, i); -			//yaffs_VerifyBlock(dev,bi,i); +	/* Now find an object value that has not already been taken +	 * by scanning the list. +	 */ -			if(bi->gcPrioritise) { -				pendingPrioritisedExist = 1; -				if(bi->blockState == YAFFS_BLOCK_STATE_FULL && -				   yaffs_BlockNotDisqualifiedFromGC(dev, bi)){ -					pagesInUse = (bi->pagesInUse - bi->softDeletions); -					dirtiest = i; -					prioritised = 1; -					aggressive = 1; /* Fool the non-aggressive skip logiv below */ +	while (!found) { +		found = 1; +		n += YAFFS_NOBJECT_BUCKETS; +		if (1 || dev->obj_bucket[bucket].count > 0) { +			list_for_each(i, &dev->obj_bucket[bucket].list) { +				/* If there is already one in the list */ +				if (i && list_entry(i, struct yaffs_obj, +						    hash_link)->obj_id == n) { +					found = 0;  				}  			}  		} - -		if(!pendingPrioritisedExist) /* None found, so we can clear this */ -			dev->hasPendingPrioritisedGCs = 0;  	} +	return n; +} -	/* If we're doing aggressive GC then we are happy to take a less-dirty block, and -	 * search harder. -	 * else (we're doing a leasurely gc), then we only bother to do this if the -	 * block has only a few pages in use. -	 */ - -	dev->nonAggressiveSkip--; - -	if (!aggressive && (dev->nonAggressiveSkip > 0)) { -		return -1; -	} - -	if(!prioritised) -		pagesInUse = -			(aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1; - -	if (aggressive) { -		iterations = -		    dev->internalEndBlock - dev->internalStartBlock + 1; -	} else { -		iterations = -		    dev->internalEndBlock - dev->internalStartBlock + 1; -		iterations = iterations / 16; -		if (iterations > 200) { -			iterations = 200; -		} -	} - -	for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) { -		b++; -		if (b < dev->internalStartBlock || b > dev->internalEndBlock) { -			b = dev->internalStartBlock; -		} - -		if (b < dev->internalStartBlock || b > dev->internalEndBlock) { -			T(YAFFS_TRACE_ERROR, -			  (TSTR("**>> Block %d is not valid" TENDSTR), b)); -			YBUG(); -		} +static void yaffs_hash_obj(struct yaffs_obj *in) +{ +	int bucket = yaffs_hash_fn(in->obj_id); +	struct yaffs_dev *dev = in->my_dev; -		bi = yaffs_GetBlockInfo(dev, b); +	list_add(&in->hash_link, &dev->obj_bucket[bucket].list); +	dev->obj_bucket[bucket].count++; +} -#if 0 -		if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) { -			dirtiest = b; -			pagesInUse = 0; -		} -		else -#endif +struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) +{ +	int bucket = yaffs_hash_fn(number); +	struct list_head *i; +	struct yaffs_obj *in; -		if (bi->blockState == YAFFS_BLOCK_STATE_FULL && -		       (bi->pagesInUse - bi->softDeletions) < pagesInUse && -			yaffs_BlockNotDisqualifiedFromGC(dev, bi)) { -			dirtiest = b; -			pagesInUse = (bi->pagesInUse - bi->softDeletions); +	list_for_each(i, &dev->obj_bucket[bucket].list) { +		/* Look if it is in the list */ +		in = list_entry(i, struct yaffs_obj, hash_link); +		if (in->obj_id == number) { +			/* Don't show if it is defered free */ +			if (in->defered_free) +				return NULL; +			return in;  		}  	} -	dev->currentDirtyChecker = b; - -	if (dirtiest > 0) { -		T(YAFFS_TRACE_GC, -		  (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest, -		   dev->nChunksPerBlock - pagesInUse,prioritised)); -	} - -	dev->oldestDirtySequence = 0; - -	if (dirtiest > 0) { -		dev->nonAggressiveSkip = 4; -	} - -	return dirtiest; +	return NULL;  } -static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo) +struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, +				enum yaffs_obj_type type)  { -	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo); - -	int erasedOk = 0; - -	/* If the block is still healthy erase it and mark as clean. -	 * If the block has had a data failure, then retire it. -	 */ - -	T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, -		(TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR), -		blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : "")); +	struct yaffs_obj *the_obj = NULL; +	struct yaffs_tnode *tn = NULL; -	bi->blockState = YAFFS_BLOCK_STATE_DIRTY; +	if (number < 0) +		number = yaffs_new_obj_id(dev); -	if (!bi->needsRetiring) { -		yaffs_InvalidateCheckpoint(dev); -		erasedOk = yaffs_EraseBlockInNAND(dev, blockNo); -		if (!erasedOk) { -			dev->nErasureFailures++; -			T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -			  (TSTR("**>> Erasure failed %d" TENDSTR), blockNo)); -		} +	if (type == YAFFS_OBJECT_TYPE_FILE) { +		tn = yaffs_get_tnode(dev); +		if (!tn) +			return NULL;  	} -	if (erasedOk && -	    ((yaffs_traceMask & YAFFS_TRACE_ERASE) || !yaffs_SkipVerification(dev))) { -		int i; -		for (i = 0; i < dev->nChunksPerBlock; i++) { -			if (!yaffs_CheckChunkErased -			    (dev, blockNo * dev->nChunksPerBlock + i)) { -				T(YAFFS_TRACE_ERROR, -				  (TSTR -				   (">>Block %d erasure supposedly OK, but chunk %d not erased" -				    TENDSTR), blockNo, i)); -			} -		} +	the_obj = yaffs_alloc_empty_obj(dev); +	if (!the_obj) { +		if (tn) +			yaffs_free_tnode(dev, tn); +		return NULL;  	} -	if (erasedOk) { -		/* Clean it up... */ -		bi->blockState = YAFFS_BLOCK_STATE_EMPTY; -		dev->nErasedBlocks++; -		bi->pagesInUse = 0; -		bi->softDeletions = 0; -		bi->hasShrinkHeader = 0; -		bi->skipErasedCheck = 1;  /* This is clean, so no need to check */ -		bi->gcPrioritise = 0; -		yaffs_ClearChunkBits(dev, blockNo); - -		T(YAFFS_TRACE_ERASE, -		  (TSTR("Erased block %d" TENDSTR), blockNo)); -	} else { -		dev->nFreeChunks -= dev->nChunksPerBlock;	/* We lost a block of free space */ +	the_obj->fake = 0; +	the_obj->rename_allowed = 1; +	the_obj->unlink_allowed = 1; +	the_obj->obj_id = number; +	yaffs_hash_obj(the_obj); +	the_obj->variant_type = type; +	yaffs_load_current_time(the_obj, 1, 1); -		yaffs_RetireBlock(dev, blockNo); -		T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -		  (TSTR("**>> Block %d retired" TENDSTR), blockNo)); +	switch (type) { +	case YAFFS_OBJECT_TYPE_FILE: +		the_obj->variant.file_variant.file_size = 0; +		the_obj->variant.file_variant.scanned_size = 0; +		the_obj->variant.file_variant.shrink_size = +						yaffs_max_file_size(dev); +		the_obj->variant.file_variant.top_level = 0; +		the_obj->variant.file_variant.top = tn; +		break; +	case YAFFS_OBJECT_TYPE_DIRECTORY: +		INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); +		INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); +		break; +	case YAFFS_OBJECT_TYPE_SYMLINK: +	case YAFFS_OBJECT_TYPE_HARDLINK: +	case YAFFS_OBJECT_TYPE_SPECIAL: +		/* No action required */ +		break; +	case YAFFS_OBJECT_TYPE_UNKNOWN: +		/* todo this should not happen */ +		break;  	} +	return the_obj;  } -static int yaffs_FindBlockForAllocation(yaffs_Device * dev) +static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, +					       int number, u32 mode)  { -	int i; - -	yaffs_BlockInfo *bi; - -	if (dev->nErasedBlocks < 1) { -		/* Hoosterman we've got a problem. -		 * Can't get space to gc -		 */ -		T(YAFFS_TRACE_ERROR, -		  (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR))); - -		return -1; -	} -	/* Find an empty block. */ - -	for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { -		dev->allocationBlockFinder++; -		if (dev->allocationBlockFinder < dev->internalStartBlock -		    || dev->allocationBlockFinder > dev->internalEndBlock) { -			dev->allocationBlockFinder = dev->internalStartBlock; -		} +	struct yaffs_obj *obj = +	    yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); -		bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder); - -		if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) { -			bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING; -			dev->sequenceNumber++; -			bi->sequenceNumber = dev->sequenceNumber; -			dev->nErasedBlocks--; -			T(YAFFS_TRACE_ALLOCATE, -			  (TSTR("Allocated block %d, seq  %d, %d left" TENDSTR), -			   dev->allocationBlockFinder, dev->sequenceNumber, -			   dev->nErasedBlocks)); -			return dev->allocationBlockFinder; -		} -	} +	if (!obj) +		return NULL; -	T(YAFFS_TRACE_ALWAYS, -	  (TSTR -	   ("yaffs tragedy: no more eraased blocks, but there should have been %d" -	    TENDSTR), dev->nErasedBlocks)); +	obj->fake = 1;	/* it is fake so it might not use NAND */ +	obj->rename_allowed = 0; +	obj->unlink_allowed = 0; +	obj->deleted = 0; +	obj->unlinked = 0; +	obj->yst_mode = mode; +	obj->my_dev = dev; +	obj->hdr_chunk = 0;	/* Not a valid chunk. */ +	return obj; -	return -1;  } -// Check if there's space to allocate... -// Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()? -static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev) +static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev)  { -	int reservedChunks; -	int reservedBlocks = dev->nReservedBlocks; -	int checkpointBlocks; - -	checkpointBlocks =  dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; -	if(checkpointBlocks < 0) -		checkpointBlocks = 0; +	int i; -	reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->nChunksPerBlock); +	dev->n_obj = 0; +	dev->n_tnodes = 0; +	yaffs_init_raw_tnodes_and_objs(dev); -	return (dev->nFreeChunks > reservedChunks); +	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { +		INIT_LIST_HEAD(&dev->obj_bucket[i].list); +		dev->obj_bucket[i].count = 0; +	}  } -static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr) +struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, +						 int number, +						 enum yaffs_obj_type type)  { -	int retVal; -	yaffs_BlockInfo *bi; +	struct yaffs_obj *the_obj = NULL; -	if (dev->allocationBlock < 0) { -		/* Get next block to allocate off */ -		dev->allocationBlock = yaffs_FindBlockForAllocation(dev); -		dev->allocationPage = 0; -	} +	if (number > 0) +		the_obj = yaffs_find_by_number(dev, number); -	if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) { -		/* Not enough space to allocate unless we're allowed to use the reserve. */ -		return -1; -	} - -	if (dev->nErasedBlocks < dev->nReservedBlocks -	    && dev->allocationPage == 0) { -		T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR))); -	} - -	/* Next page please.... */ -	if (dev->allocationBlock >= 0) { -		bi = yaffs_GetBlockInfo(dev, dev->allocationBlock); - -		retVal = (dev->allocationBlock * dev->nChunksPerBlock) + -		    dev->allocationPage; -		bi->pagesInUse++; -		yaffs_SetChunkBit(dev, dev->allocationBlock, -				  dev->allocationPage); +	if (!the_obj) +		the_obj = yaffs_new_obj(dev, number, type); -		dev->allocationPage++; +	return the_obj; -		dev->nFreeChunks--; - -		/* If the block is full set the state to full */ -		if (dev->allocationPage >= dev->nChunksPerBlock) { -			bi->blockState = YAFFS_BLOCK_STATE_FULL; -			dev->allocationBlock = -1; -		} - -		if(blockUsedPtr) -			*blockUsedPtr = bi; - -		return retVal; -	} - -	T(YAFFS_TRACE_ERROR, -	  (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); - -	return -1;  } -static int yaffs_GetErasedChunks(yaffs_Device * dev) +YCHAR *yaffs_clone_str(const YCHAR *str)  { -	int n; +	YCHAR *new_str = NULL; +	int len; -	n = dev->nErasedBlocks * dev->nChunksPerBlock; +	if (!str) +		str = _Y(""); -	if (dev->allocationBlock > 0) { -		n += (dev->nChunksPerBlock - dev->allocationPage); +	len = yaffs_strnlen(str, YAFFS_MAX_ALIAS_LENGTH); +	new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); +	if (new_str) { +		yaffs_strncpy(new_str, str, len); +		new_str[len] = 0;  	} - -	return n; +	return new_str;  } +/* + *yaffs_update_parent() handles fixing a directories mtime and ctime when a new + * link (ie. name) is created or deleted in the directory. + * + * ie. + *   create dir/a : update dir's mtime/ctime + *   rm dir/a:   update dir's mtime/ctime + *   modify dir/a: don't update dir's mtimme/ctime + * + * This can be handled immediately or defered. Defering helps reduce the number + * of updates when many files in a directory are changed within a brief period. + * + * If the directory updating is defered then yaffs_update_dirty_dirs must be + * called periodically. + */ -static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) +static void yaffs_update_parent(struct yaffs_obj *obj)  { -	int oldChunk; -	int newChunk; -	int chunkInBlock; -	int markNAND; -	int retVal = YAFFS_OK; -	int cleanups = 0; -	int i; -	int isCheckpointBlock; -	int matchingChunk; - -	int chunksBefore = yaffs_GetErasedChunks(dev); -	int chunksAfter; - -	yaffs_ExtendedTags tags; +	struct yaffs_dev *dev; -	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block); - -	yaffs_Object *object; - -	isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT); - -	bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; - -	T(YAFFS_TRACE_TRACING, -	  (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block, -	   bi->pagesInUse, bi->hasShrinkHeader)); - -	/*yaffs_VerifyFreeChunks(dev); */ - -	bi->hasShrinkHeader = 0;	/* clear the flag so that the block can erase */ - -	/* Take off the number of soft deleted entries because -	 * they're going to get really deleted during GC. -	 */ -	dev->nFreeChunks -= bi->softDeletions; +	if (!obj) +		return; +	dev = obj->my_dev; +	obj->dirty = 1; +	yaffs_load_current_time(obj, 0, 1); +	if (dev->param.defered_dir_update) { +		struct list_head *link = &obj->variant.dir_variant.dirty; -	dev->isDoingGC = 1; +		if (list_empty(link)) { +			list_add(link, &dev->dirty_dirs); +			yaffs_trace(YAFFS_TRACE_BACKGROUND, +			  "Added object %d to dirty directories", +			   obj->obj_id); +		} -	if (isCheckpointBlock || -	    !yaffs_StillSomeChunkBits(dev, block)) { -		T(YAFFS_TRACE_TRACING, -		  (TSTR -		   ("Collecting block %d that has no chunks in use" TENDSTR), -		   block)); -		yaffs_BlockBecameDirty(dev, block);  	} else { +		yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); +	} +} -		__u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); - -		yaffs_VerifyBlock(dev,bi,block); - -		for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock; -		     chunkInBlock < dev->nChunksPerBlock -		     && yaffs_StillSomeChunkBits(dev, block); -		     chunkInBlock++, oldChunk++) { -			if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) { - -				/* This page is in use and might need to be copied off */ - -				markNAND = 1; - -				yaffs_InitialiseTags(&tags); - -				yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk, -								buffer, &tags); - -				object = -				    yaffs_FindObjectByNumber(dev, -							     tags.objectId); - -				T(YAFFS_TRACE_GC_DETAIL, -				  (TSTR -				   ("Collecting page %d, %d %d %d " TENDSTR), -				   chunkInBlock, tags.objectId, tags.chunkId, -				   tags.byteCount)); - -				if(object && !yaffs_SkipVerification(dev)){ -					if(tags.chunkId == 0) -						matchingChunk = object->chunkId; -					else if(object->softDeleted) -						matchingChunk = oldChunk; /* Defeat the test */ -					else -						matchingChunk = yaffs_FindChunkInFile(object,tags.chunkId,NULL); - -					if(oldChunk != matchingChunk) -						T(YAFFS_TRACE_ERROR, -						  (TSTR("gc: page in gc mismatch: %d %d %d %d"TENDSTR), -						  oldChunk,matchingChunk,tags.objectId, tags.chunkId)); +void yaffs_update_dirty_dirs(struct yaffs_dev *dev) +{ +	struct list_head *link; +	struct yaffs_obj *obj; +	struct yaffs_dir_var *d_s; +	union yaffs_obj_var *o_v; -				} +	yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); -				if (!object) { -					T(YAFFS_TRACE_ERROR, -					  (TSTR -					   ("page %d in gc has no object: %d %d %d " -					    TENDSTR), oldChunk, -					    tags.objectId, tags.chunkId, tags.byteCount)); -				} +	while (!list_empty(&dev->dirty_dirs)) { +		link = dev->dirty_dirs.next; +		list_del_init(link); -				if (object && object->deleted -				    && tags.chunkId != 0) { -					/* Data chunk in a deleted file, throw it away -					 * It's a soft deleted data chunk, -					 * No need to copy this, just forget about it and -					 * fix up the object. -					 */ +		d_s = list_entry(link, struct yaffs_dir_var, dirty); +		o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); +		obj = list_entry(o_v, struct yaffs_obj, variant); -					object->nDataChunks--; +		yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", +			obj->obj_id); -					if (object->nDataChunks <= 0) { -						/* remeber to clean up the object */ -						dev->gcCleanupList[cleanups] = -						    tags.objectId; -						cleanups++; -					} -					markNAND = 0; -				} else if (0 -					   /* Todo object && object->deleted && object->nDataChunks == 0 */ -					   ) { -					/* Deleted object header with no data chunks. -					 * Can be discarded and the file deleted. -					 */ -					object->chunkId = 0; -					yaffs_FreeTnode(object->myDev, -							object->variant. -							fileVariant.top); -					object->variant.fileVariant.top = NULL; -					yaffs_DoGenericObjectDeletion(object); +		if (obj->dirty) +			yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); +	} +} -				} else if (object) { -					/* It's either a data chunk in a live file or -					 * an ObjectHeader, so we're interested in it. -					 * NB Need to keep the ObjectHeaders of deleted files -					 * until the whole file has been deleted off -					 */ -					tags.serialNumber++; +/* + * Mknod (create) a new object. + * equiv_obj only has meaning for a hard link; + * alias_str only has meaning for a symlink. + * rdev only has meaning for devices (a subset of special objects) + */ -					dev->nGCCopies++; +static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, +					  struct yaffs_obj *parent, +					  const YCHAR *name, +					  u32 mode, +					  u32 uid, +					  u32 gid, +					  struct yaffs_obj *equiv_obj, +					  const YCHAR *alias_str, u32 rdev) +{ +	struct yaffs_obj *in; +	YCHAR *str = NULL; +	struct yaffs_dev *dev = parent->my_dev; -					if (tags.chunkId == 0) { -						/* It is an object Id, -						 * We need to nuke the shrinkheader flags first -						 * We no longer want the shrinkHeader flag since its work is done -						 * and if it is left in place it will mess up scanning. -						 * Also, clear out any shadowing stuff -						 */ +	/* Check if the entry exists. +	 * If it does then fail the call since we don't want a dup. */ +	if (yaffs_find_by_name(parent, name)) +		return NULL; -						yaffs_ObjectHeader *oh; -						oh = (yaffs_ObjectHeader *)buffer; -						oh->isShrink = 0; -						oh->shadowsObject = -1; -						tags.extraShadows = 0; -						tags.extraIsShrinkHeader = 0; +	if (type == YAFFS_OBJECT_TYPE_SYMLINK) { +		str = yaffs_clone_str(alias_str); +		if (!str) +			return NULL; +	} -						yaffs_VerifyObjectHeader(object,oh,&tags,1); -					} +	in = yaffs_new_obj(dev, -1, type); -					newChunk = -					    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); +	if (!in) { +		kfree(str); +		return NULL; +	} -					if (newChunk < 0) { -						retVal = YAFFS_FAIL; -					} else { +	in->hdr_chunk = 0; +	in->valid = 1; +	in->variant_type = type; -						/* Ok, now fix up the Tnodes etc. */ +	in->yst_mode = mode; -						if (tags.chunkId == 0) { -							/* It's a header */ -							object->chunkId =  newChunk; -							object->serial =   tags.serialNumber; -						} else { -							/* It's a data chunk */ -							yaffs_PutChunkIntoFile -							    (object, -							     tags.chunkId, -							     newChunk, 0); -						} -					} -				} +	yaffs_attribs_init(in, gid, uid, rdev); -				yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__); +	in->n_data_chunks = 0; -			} -		} +	yaffs_set_obj_name(in, name); +	in->dirty = 1; -		yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); +	yaffs_add_obj_to_dir(parent, in); +	in->my_dev = parent->my_dev; -		/* Do any required cleanups */ -		for (i = 0; i < cleanups; i++) { -			/* Time to delete the file too */ -			object = -			    yaffs_FindObjectByNumber(dev, -						     dev->gcCleanupList[i]); -			if (object) { -				yaffs_FreeTnode(dev, -						object->variant.fileVariant. -						top); -				object->variant.fileVariant.top = NULL; -				T(YAFFS_TRACE_GC, -				  (TSTR -				   ("yaffs: About to finally delete object %d" -				    TENDSTR), object->objectId)); -				yaffs_DoGenericObjectDeletion(object); -				object->myDev->nDeletedFiles--; -			} - -		} - +	switch (type) { +	case YAFFS_OBJECT_TYPE_SYMLINK: +		in->variant.symlink_variant.alias = str; +		break; +	case YAFFS_OBJECT_TYPE_HARDLINK: +		in->variant.hardlink_variant.equiv_obj = equiv_obj; +		in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; +		list_add(&in->hard_links, &equiv_obj->hard_links); +		break; +	case YAFFS_OBJECT_TYPE_FILE: +	case YAFFS_OBJECT_TYPE_DIRECTORY: +	case YAFFS_OBJECT_TYPE_SPECIAL: +	case YAFFS_OBJECT_TYPE_UNKNOWN: +		/* do nothing */ +		break;  	} -	yaffs_VerifyCollectedBlock(dev,bi,block); - -	if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) { -		T(YAFFS_TRACE_GC, -		  (TSTR -		   ("gc did not increase free chunks before %d after %d" -		    TENDSTR), chunksBefore, chunksAfter)); +	if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { +		/* Could not create the object header, fail */ +		yaffs_del_obj(in); +		in = NULL;  	} -	dev->isDoingGC = 0; +	if (in) +		yaffs_update_parent(parent); -	return retVal; +	return in;  } -/* New garbage collector - * If we're very low on erased blocks then we do aggressive garbage collection - * otherwise we do "leasurely" garbage collection. - * Aggressive gc looks further (whole array) and will accept less dirty blocks. - * Passive gc only inspects smaller areas and will only accept more dirty blocks. - * - * The idea is to help clear out space in a more spread-out manner. - * Dunno if it really does anything useful. - */ -static int yaffs_CheckGarbageCollection(yaffs_Device * dev) +struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, +				    const YCHAR *name, u32 mode, u32 uid, +				    u32 gid)  { -	int block; -	int aggressive; -	int gcOk = YAFFS_OK; -	int maxTries = 0; - -	int checkpointBlockAdjust; - -	if (dev->isDoingGC) { -		/* Bail out so we don't get recursive gc */ -		return YAFFS_OK; -	} - -	/* This loop should pass the first time. -	 * We'll only see looping here if the erase of the collected block fails. -	 */ - -	do { -		maxTries++; - -		checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint); -		if(checkpointBlockAdjust < 0) -			checkpointBlockAdjust = 0; +	return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, +				uid, gid, NULL, NULL, 0); +} -		if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust + 2)) { -			/* We need a block soon...*/ -			aggressive = 1; -		} else { -			/* We're in no hurry */ -			aggressive = 0; -		} +struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, +				   u32 mode, u32 uid, u32 gid) +{ +	return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, +				mode, uid, gid, NULL, NULL, 0); +} -		block = yaffs_FindBlockForGarbageCollection(dev, aggressive); +struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, +				       const YCHAR *name, u32 mode, u32 uid, +				       u32 gid, u32 rdev) +{ +	return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, +				uid, gid, NULL, NULL, rdev); +} -		if (block > 0) { -			dev->garbageCollections++; -			if (!aggressive) { -				dev->passiveGarbageCollections++; -			} +struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, +				       const YCHAR *name, u32 mode, u32 uid, +				       u32 gid, const YCHAR *alias) +{ +	return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, +				uid, gid, NULL, alias, 0); +} -			T(YAFFS_TRACE_GC, -			  (TSTR -			   ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR), -			   dev->nErasedBlocks, aggressive)); +/* yaffs_link_obj returns the object id of the equivalent object.*/ +struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, +				 struct yaffs_obj *equiv_obj) +{ +	/* Get the real object in case we were fed a hard link obj */ +	equiv_obj = yaffs_get_equivalent_obj(equiv_obj); -			gcOk = yaffs_GarbageCollectBlock(dev, block); -		} +	if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, +			parent, name, 0, 0, 0, +			equiv_obj, NULL, 0)) +		return equiv_obj; -		if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) { -			T(YAFFS_TRACE_GC, -			  (TSTR -			   ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" -			    TENDSTR), dev->nErasedBlocks, maxTries, block)); -		} -	} while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0) -		 && (maxTries < 2)); +	return NULL; -	return aggressive ? gcOk : YAFFS_OK;  } -/*-------------------------  TAGS --------------------------------*/ -static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, -			   int chunkInObject) -{ -	return (tags->chunkId == chunkInObject && -		tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0; - -} +/*---------------------- Block Management and Page Allocation -------------*/ -/*-------------------- Data file manipulation -----------------*/ - -static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, -				 yaffs_ExtendedTags * tags) +static void yaffs_deinit_blocks(struct yaffs_dev *dev)  { -	/*Get the Tnode, then get the level 0 offset chunk offset */ -	yaffs_Tnode *tn; -	int theChunk = -1; -	yaffs_ExtendedTags localTags; -	int retVal = -1; - -	yaffs_Device *dev = in->myDev; - -	if (!tags) { -		/* Passed a NULL, so use our own tags space */ -		tags = &localTags; -	} +	if (dev->block_info_alt && dev->block_info) +		vfree(dev->block_info); +	else +		kfree(dev->block_info); -	tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); +	dev->block_info_alt = 0; -	if (tn) { -		theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); +	dev->block_info = NULL; -		retVal = -		    yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, -					   chunkInInode); -	} -	return retVal; +	if (dev->chunk_bits_alt && dev->chunk_bits) +		vfree(dev->chunk_bits); +	else +		kfree(dev->chunk_bits); +	dev->chunk_bits_alt = 0; +	dev->chunk_bits = NULL;  } -static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode, -					  yaffs_ExtendedTags * tags) +static int yaffs_init_blocks(struct yaffs_dev *dev)  { -	/* Get the Tnode, then get the level 0 offset chunk offset */ -	yaffs_Tnode *tn; -	int theChunk = -1; -	yaffs_ExtendedTags localTags; +	int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -	yaffs_Device *dev = in->myDev; -	int retVal = -1; +	dev->block_info = NULL; +	dev->chunk_bits = NULL; +	dev->alloc_block = -1;	/* force it to get a new one */ -	if (!tags) { -		/* Passed a NULL, so use our own tags space */ -		tags = &localTags; +	/* If the first allocation strategy fails, thry the alternate one */ +	dev->block_info = +		kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); +	if (!dev->block_info) { +		dev->block_info = +		    vmalloc(n_blocks * sizeof(struct yaffs_block_info)); +		dev->block_info_alt = 1; +	} else { +		dev->block_info_alt = 0;  	} -	tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); - -	if (tn) { - -		theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); - -		retVal = -		    yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, -					   chunkInInode); +	if (!dev->block_info) +		goto alloc_error; -		/* Delete the entry in the filestructure (if found) */ -		if (retVal != -1) { -			yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0); -		} +	/* Set up dynamic blockinfo stuff. Round up bytes. */ +	dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; +	dev->chunk_bits = +		kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); +	if (!dev->chunk_bits) { +		dev->chunk_bits = +		    vmalloc(dev->chunk_bit_stride * n_blocks); +		dev->chunk_bits_alt = 1;  	} else { -		/*T(("No level 0 found for %d\n", chunkInInode)); */ +		dev->chunk_bits_alt = 0;  	} +	if (!dev->chunk_bits) +		goto alloc_error; -	if (retVal == -1) { -		/* T(("Could not find %d to delete\n",chunkInInode)); */ -	} -	return retVal; + +	memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); +	memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); +	return YAFFS_OK; + +alloc_error: +	yaffs_deinit_blocks(dev); +	return YAFFS_FAIL;  } -#ifdef YAFFS_PARANOID -static int yaffs_CheckFileSanity(yaffs_Object * in) +void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no)  { -	int chunk; -	int nChunks; -	int fSize; -	int failed = 0; -	int objId; -	yaffs_Tnode *tn; -	yaffs_Tags localTags; -	yaffs_Tags *tags = &localTags; -	int theChunk; -	int chunkDeleted; - -	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { -		/* T(("Object not a file\n")); */ -		return YAFFS_FAIL; -	} +	struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); +	int erased_ok = 0; +	int i; -	objId = in->objectId; -	fSize = in->variant.fileVariant.fileSize; -	nChunks = -	    (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk; +	/* If the block is still healthy erase it and mark as clean. +	 * If the block has had a data failure, then retire it. +	 */ -	for (chunk = 1; chunk <= nChunks; chunk++) { -		tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant, -					   chunk); +	yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, +		"yaffs_block_became_dirty block %d state %d %s", +		block_no, bi->block_state, +		(bi->needs_retiring) ? "needs retiring" : ""); -		if (tn) { +	yaffs2_clear_oldest_dirty_seq(dev, bi); -			theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk); +	bi->block_state = YAFFS_BLOCK_STATE_DIRTY; -			if (yaffs_CheckChunkBits -			    (dev, theChunk / dev->nChunksPerBlock, -			     theChunk % dev->nChunksPerBlock)) { +	/* If this is the block being garbage collected then stop gc'ing */ +	if (block_no == dev->gc_block) +		dev->gc_block = 0; -				yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk, -							    tags, -							    &chunkDeleted); -				if (yaffs_TagsMatch -				    (tags, in->objectId, chunk, chunkDeleted)) { -					/* found it; */ +	/* If this block is currently the best candidate for gc +	 * then drop as a candidate */ +	if (block_no == dev->gc_dirtiest) { +		dev->gc_dirtiest = 0; +		dev->gc_pages_in_use = 0; +	} -				} -			} else { +	if (!bi->needs_retiring) { +		yaffs2_checkpt_invalidate(dev); +		erased_ok = yaffs_erase_block(dev, block_no); +		if (!erased_ok) { +			dev->n_erase_failures++; +			yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, +			  "**>> Erasure failed %d", block_no); +		} +	} -				failed = 1; +	/* Verify erasure if needed */ +	if (erased_ok && +	    ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || +	     !yaffs_skip_verification(dev))) { +		for (i = 0; i < dev->param.chunks_per_block; i++) { +			if (!yaffs_check_chunk_erased(dev, +				block_no * dev->param.chunks_per_block + i)) { +				yaffs_trace(YAFFS_TRACE_ERROR, +					">>Block %d erasure supposedly OK, but chunk %d not erased", +					block_no, i);  			} - -		} else { -			/* T(("No level 0 found for %d\n", chunk)); */  		}  	} -	return failed ? YAFFS_FAIL : YAFFS_OK; -} +	if (!erased_ok) { +		/* We lost a block of free space */ +		dev->n_free_chunks -= dev->param.chunks_per_block; +		yaffs_retire_block(dev, block_no); +		yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, +			"**>> Block %d retired", block_no); +		return; +	} -#endif +	/* Clean it up... */ +	bi->block_state = YAFFS_BLOCK_STATE_EMPTY; +	bi->seq_number = 0; +	dev->n_erased_blocks++; +	bi->pages_in_use = 0; +	bi->soft_del_pages = 0; +	bi->has_shrink_hdr = 0; +	bi->skip_erased_check = 1;	/* Clean, so no need to check */ +	bi->gc_prioritise = 0; +	bi->has_summary = 0; + +	yaffs_clear_chunk_bits(dev, block_no); + +	yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); +} -static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, -				  int chunkInNAND, int inScan) +static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, +					struct yaffs_block_info *bi, +					int old_chunk, u8 *buffer)  { -	/* NB inScan is zero unless scanning. -	 * For forward scanning, inScan is > 0; -	 * for backward scanning inScan is < 0 -	 */ +	int new_chunk; +	int mark_flash = 1; +	struct yaffs_ext_tags tags; +	struct yaffs_obj *object; +	int matching_chunk; +	int ret_val = YAFFS_OK; -	yaffs_Tnode *tn; -	yaffs_Device *dev = in->myDev; -	int existingChunk; -	yaffs_ExtendedTags existingTags; -	yaffs_ExtendedTags newTags; -	unsigned existingSerial, newSerial; +	memset(&tags, 0, sizeof(tags)); +	yaffs_rd_chunk_tags_nand(dev, old_chunk, +				 buffer, &tags); +	object = yaffs_find_by_number(dev, tags.obj_id); -	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { -		/* Just ignore an attempt at putting a chunk into a non-file during scanning -		 * If it is not during Scanning then something went wrong! -		 */ -		if (!inScan) { -			T(YAFFS_TRACE_ERROR, -			  (TSTR -			   ("yaffs tragedy:attempt to put data chunk into a non-file" -			    TENDSTR))); -			YBUG(); -		} +	yaffs_trace(YAFFS_TRACE_GC_DETAIL, +		"Collecting chunk in block %d, %d %d %d ", +		dev->gc_chunk, tags.obj_id, +		tags.chunk_id, tags.n_bytes); -		yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); -		return YAFFS_OK; +	if (object && !yaffs_skip_verification(dev)) { +		if (tags.chunk_id == 0) +			matching_chunk = +			    object->hdr_chunk; +		else if (object->soft_del) +			/* Defeat the test */ +			matching_chunk = old_chunk; +		else +			matching_chunk = +			    yaffs_find_chunk_in_file +			    (object, tags.chunk_id, +			     NULL); + +		if (old_chunk != matching_chunk) +			yaffs_trace(YAFFS_TRACE_ERROR, +				"gc: page in gc mismatch: %d %d %d %d", +				old_chunk, +				matching_chunk, +				tags.obj_id, +				tags.chunk_id);  	} -	tn = yaffs_AddOrFindLevel0Tnode(dev, -					&in->variant.fileVariant, -					chunkInInode, -					NULL); -	if (!tn) { -		return YAFFS_FAIL; +	if (!object) { +		yaffs_trace(YAFFS_TRACE_ERROR, +			"page %d in gc has no object: %d %d %d ", +			old_chunk, +			tags.obj_id, tags.chunk_id, +			tags.n_bytes);  	} -	existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); +	if (object && +	    object->deleted && +	    object->soft_del && tags.chunk_id != 0) { +		/* Data chunk in a soft deleted file, +		 * throw it away. +		 * It's a soft deleted data chunk, +		 * No need to copy this, just forget +		 * about it and fix up the object. +		 */ -	if (inScan != 0) { -		/* If we're scanning then we need to test for duplicates -		 * NB This does not need to be efficient since it should only ever -		 * happen when the power fails during a write, then only one -		 * chunk should ever be affected. -		 * -		 * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO -		 * Update: For backward scanning we don't need to re-read tags so this is quite cheap. +		/* Free chunks already includes +		 * softdeleted chunks, how ever this +		 * chunk is going to soon be really +		 * deleted which will increment free +		 * chunks. We have to decrement free +		 * chunks so this works out properly.  		 */ +		dev->n_free_chunks--; +		bi->soft_del_pages--; -		if (existingChunk != 0) { -			/* NB Right now existing chunk will not be real chunkId if the device >= 32MB -			 *    thus we have to do a FindChunkInFile to get the real chunk id. -			 * -			 * We have a duplicate now we need to decide which one to use: -			 * -			 * Backwards scanning YAFFS2: The old one is what we use, dump the new one. -			 * Forward scanning YAFFS2: The new one is what we use, dump the old one. -			 * YAFFS1: Get both sets of tags and compare serial numbers. +		object->n_data_chunks--; +		if (object->n_data_chunks <= 0) { +			/* remeber to clean up obj */ +			dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; +			dev->n_clean_ups++; +		} +		mark_flash = 0; +	} else if (object) { +		/* It's either a data chunk in a live +		 * file or an ObjectHeader, so we're +		 * interested in it. +		 * NB Need to keep the ObjectHeaders of +		 * deleted files until the whole file +		 * has been deleted off +		 */ +		tags.serial_number++; +		dev->n_gc_copies++; + +		if (tags.chunk_id == 0) { +			/* It is an object Id, +			 * We need to nuke the +			 * shrinkheader flags since its +			 * work is done. +			 * Also need to clean up +			 * shadowing.  			 */ +			struct yaffs_obj_hdr *oh; +			oh = (struct yaffs_obj_hdr *) buffer; -			if (inScan > 0) { -				/* Only do this for forward scanning */ -				yaffs_ReadChunkWithTagsFromNAND(dev, -								chunkInNAND, -								NULL, &newTags); +			oh->is_shrink = 0; +			tags.extra_is_shrink = 0; +			oh->shadows_obj = 0; +			oh->inband_shadowed_obj_id = 0; +			tags.extra_shadows = 0; -				/* Do a proper find */ -				existingChunk = -				    yaffs_FindChunkInFile(in, chunkInInode, -							  &existingTags); +			/* Update file size */ +			if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { +				yaffs_oh_size_load(oh, +				    object->variant.file_variant.file_size); +				tags.extra_file_size = +				    object->variant.file_variant.file_size;  			} -			if (existingChunk <= 0) { -				/*Hoosterman - how did this happen? */ - -				T(YAFFS_TRACE_ERROR, -				  (TSTR -				   ("yaffs tragedy: existing chunk < 0 in scan" -				    TENDSTR))); - -			} +			yaffs_verify_oh(object, oh, &tags, 1); +			new_chunk = +			    yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); +		} else { +			new_chunk = +			    yaffs_write_new_chunk(dev, buffer, &tags, 1); +		} -			/* NB The deleted flags should be false, otherwise the chunks will -			 * not be loaded during a scan -			 */ +		if (new_chunk < 0) { +			ret_val = YAFFS_FAIL; +		} else { -			newSerial = newTags.serialNumber; -			existingSerial = existingTags.serialNumber; +			/* Now fix up the Tnodes etc. */ -			if ((inScan > 0) && -			    (in->myDev->isYaffs2 || -			     existingChunk <= 0 || -			     ((existingSerial + 1) & 3) == newSerial)) { -				/* Forward scanning. -				 * Use new -				 * Delete the old one and drop through to update the tnode -				 */ -				yaffs_DeleteChunk(dev, existingChunk, 1, -						  __LINE__); +			if (tags.chunk_id == 0) { +				/* It's a header */ +				object->hdr_chunk = new_chunk; +				object->serial = tags.serial_number;  			} else { -				/* Backward scanning or we want to use the existing one -				 * Use existing. -				 * Delete the new one and return early so that the tnode isn't changed -				 */ -				yaffs_DeleteChunk(dev, chunkInNAND, 1, -						  __LINE__); -				return YAFFS_OK; +				/* It's a data chunk */ +				yaffs_put_chunk_in_file(object, tags.chunk_id, +							new_chunk, 0);  			}  		} - -	} - -	if (existingChunk == 0) { -		in->nDataChunks++; -	} - -	yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND); - -	return YAFFS_OK; -} - -static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode, -					 __u8 * buffer) -{ -	int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL); - -	if (chunkInNAND >= 0) { -		return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND, -						       buffer,NULL); -	} else { -		T(YAFFS_TRACE_NANDACCESS, -		  (TSTR("Chunk %d not found zero instead" TENDSTR), -		   chunkInNAND)); -		/* get sane (zero) data if you read a hole */ -		memset(buffer, 0, in->myDev->nDataBytesPerChunk); -		return 0;  	} - +	if (ret_val == YAFFS_OK) +		yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); +	return ret_val;  } -void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn) +static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block)  { -	int block; -	int page; -	yaffs_ExtendedTags tags; -	yaffs_BlockInfo *bi; - -	if (chunkId <= 0) -		return; - - -	dev->nDeletions++; -	block = chunkId / dev->nChunksPerBlock; -	page = chunkId % dev->nChunksPerBlock; +	int old_chunk; +	int ret_val = YAFFS_OK; +	int i; +	int is_checkpt_block; +	int max_copies; +	int chunks_before = yaffs_get_erased_chunks(dev); +	int chunks_after; +	struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); +	is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); -	if(!yaffs_CheckChunkBit(dev,block,page)) -		T(YAFFS_TRACE_VERIFY, -			(TSTR("Deleting invalid chunk %d"TENDSTR), -			 chunkId)); +	yaffs_trace(YAFFS_TRACE_TRACING, +		"Collecting block %d, in use %d, shrink %d, whole_block %d", +		block, bi->pages_in_use, bi->has_shrink_hdr, +		whole_block); -	bi = yaffs_GetBlockInfo(dev, block); +	/*yaffs_verify_free_chunks(dev); */ -	T(YAFFS_TRACE_DELETION, -	  (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId)); +	if (bi->block_state == YAFFS_BLOCK_STATE_FULL) +		bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; -	if (markNAND && -	    bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) { +	bi->has_shrink_hdr = 0;	/* clear the flag so that the block can erase */ -		yaffs_InitialiseTags(&tags); +	dev->gc_disable = 1; -		tags.chunkDeleted = 1; +	yaffs_summary_gc(dev, block); -		yaffs_WriteChunkWithTagsToNAND(dev, chunkId, NULL, &tags); -		yaffs_HandleUpdateChunk(dev, chunkId, &tags); +	if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { +		yaffs_trace(YAFFS_TRACE_TRACING, +			"Collecting block %d that has no chunks in use", +			block); +		yaffs_block_became_dirty(dev, block);  	} else { -		dev->nUnmarkedDeletions++; -	} -	/* Pull out of the management area. -	 * If the whole block became dirty, this will kick off an erasure. -	 */ -	if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || -	    bi->blockState == YAFFS_BLOCK_STATE_FULL || -	    bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING || -	    bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) { -		dev->nFreeChunks++; +		u8 *buffer = yaffs_get_temp_buffer(dev); -		yaffs_ClearChunkBit(dev, block, page); +		yaffs_verify_blk(dev, bi, block); -		bi->pagesInUse--; +		max_copies = (whole_block) ? dev->param.chunks_per_block : 5; +		old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; -		if (bi->pagesInUse == 0 && -		    !bi->hasShrinkHeader && -		    bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING && -		    bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) { -			yaffs_BlockBecameDirty(dev, block); +		for (/* init already done */ ; +		     ret_val == YAFFS_OK && +		     dev->gc_chunk < dev->param.chunks_per_block && +		     (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && +		     max_copies > 0; +		     dev->gc_chunk++, old_chunk++) { +			if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { +				/* Page is in use and might need to be copied */ +				max_copies--; +				ret_val = yaffs_gc_process_chunk(dev, bi, +							old_chunk, buffer); +			}  		} - -	} else { -		/* T(("Bad news deleting chunk %d\n",chunkId)); */ +		yaffs_release_temp_buffer(dev, buffer);  	} -} - -static int yaffs_WriteChunkDataToObject(yaffs_Object * in, int chunkInInode, -					const __u8 * buffer, int nBytes, -					int useReserve) -{ -	/* Find old chunk Need to do this to get serial number -	 * Write new one and patch into tree. -	 * Invalidate old tags. -	 */ - -	int prevChunkId; -	yaffs_ExtendedTags prevTags; - -	int newChunkId; -	yaffs_ExtendedTags newTags; - -	yaffs_Device *dev = in->myDev; - -	yaffs_CheckGarbageCollection(dev); - -	/* Get the previous chunk at this location in the file if it exists */ -	prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags); +	yaffs_verify_collected_blk(dev, bi, block); -	/* Set up new tags */ -	yaffs_InitialiseTags(&newTags); - -	newTags.chunkId = chunkInInode; -	newTags.objectId = in->objectId; -	newTags.serialNumber = -	    (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1; -	newTags.byteCount = nBytes; - -	newChunkId = -	    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, -					      useReserve); - -	if (newChunkId >= 0) { -		yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0); - -		if (prevChunkId >= 0) { -			yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__); +	if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { +		/* +		 * The gc did not complete. Set block state back to FULL +		 * because checkpointing does not restore gc. +		 */ +		bi->block_state = YAFFS_BLOCK_STATE_FULL; +	} else { +		/* The gc completed. */ +		/* Do any required cleanups */ +		for (i = 0; i < dev->n_clean_ups; i++) { +			/* Time to delete the file too */ +			struct yaffs_obj *object = +			    yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); +			if (object) { +				yaffs_free_tnode(dev, +					  object->variant.file_variant.top); +				object->variant.file_variant.top = NULL; +				yaffs_trace(YAFFS_TRACE_GC, +					"yaffs: About to finally delete object %d", +					object->obj_id); +				yaffs_generic_obj_del(object); +				object->my_dev->n_deleted_files--; +			}  		} - -		yaffs_CheckFileSanity(in); +		chunks_after = yaffs_get_erased_chunks(dev); +		if (chunks_before >= chunks_after) +			yaffs_trace(YAFFS_TRACE_GC, +				"gc did not increase free chunks before %d after %d", +				chunks_before, chunks_after); +		dev->gc_block = 0; +		dev->gc_chunk = 0; +		dev->n_clean_ups = 0;  	} -	return newChunkId; +	dev->gc_disable = 0; + +	return ret_val;  } -/* UpdateObjectHeader updates the header on NAND for an object. - * If name is not NULL, then that new name is used. +/* + * find_gc_block() selects the dirtiest block (or close enough) + * for garbage collection.   */ -int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, -			     int isShrink, int shadows) -{ -	yaffs_BlockInfo *bi; - -	yaffs_Device *dev = in->myDev; - -	int prevChunkId; -	int retVal = 0; - -	int newChunkId; -	yaffs_ExtendedTags newTags; -	yaffs_ExtendedTags oldTags; - -	__u8 *buffer = NULL; -	YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1]; - -	yaffs_ObjectHeader *oh = NULL; - -	yaffs_strcpy(oldName,"silly old name"); - -	if (!in->fake || force) { - -		yaffs_CheckGarbageCollection(dev); -		yaffs_CheckObjectDetailsLoaded(in); - -		buffer = yaffs_GetTempBuffer(in->myDev, __LINE__); -		oh = (yaffs_ObjectHeader *) buffer; - -		prevChunkId = in->chunkId; - -		if (prevChunkId >= 0) { -			yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId, -							buffer, &oldTags); +static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, +				    int aggressive, int background) +{ +	int i; +	int iterations; +	unsigned selected = 0; +	int prioritised = 0; +	int prioritised_exist = 0; +	struct yaffs_block_info *bi; +	int threshold; -			yaffs_VerifyObjectHeader(in,oh,&oldTags,0); +	/* First let's see if we need to grab a prioritised block */ +	if (dev->has_pending_prioritised_gc && !aggressive) { +		dev->gc_dirtiest = 0; +		bi = dev->block_info; +		for (i = dev->internal_start_block; +		     i <= dev->internal_end_block && !selected; i++) { -			memcpy(oldName, oh->name, sizeof(oh->name)); +			if (bi->gc_prioritise) { +				prioritised_exist = 1; +				if (bi->block_state == YAFFS_BLOCK_STATE_FULL && +				    yaffs_block_ok_for_gc(dev, bi)) { +					selected = i; +					prioritised = 1; +				} +			} +			bi++;  		} -		memset(buffer, 0xFF, dev->nDataBytesPerChunk); +		/* +		 * If there is a prioritised block and none was selected then +		 * this happened because there is at least one old dirty block +		 * gumming up the works. Let's gc the oldest dirty block. +		 */ -		oh->type = in->variantType; -		oh->yst_mode = in->yst_mode; -		oh->shadowsObject = shadows; +		if (prioritised_exist && +		    !selected && dev->oldest_dirty_block > 0) +			selected = dev->oldest_dirty_block; -#ifdef CONFIG_YAFFS_WINCE -		oh->win_atime[0] = in->win_atime[0]; -		oh->win_ctime[0] = in->win_ctime[0]; -		oh->win_mtime[0] = in->win_mtime[0]; -		oh->win_atime[1] = in->win_atime[1]; -		oh->win_ctime[1] = in->win_ctime[1]; -		oh->win_mtime[1] = in->win_mtime[1]; -#else -		oh->yst_uid = in->yst_uid; -		oh->yst_gid = in->yst_gid; -		oh->yst_atime = in->yst_atime; -		oh->yst_mtime = in->yst_mtime; -		oh->yst_ctime = in->yst_ctime; -		oh->yst_rdev = in->yst_rdev; -#endif -		if (in->parent) { -			oh->parentObjectId = in->parent->objectId; -		} else { -			oh->parentObjectId = 0; -		} - -		if (name && *name) { -			memset(oh->name, 0, sizeof(oh->name)); -			yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); -		} else if (prevChunkId>=0) { -			memcpy(oh->name, oldName, sizeof(oh->name)); -		} else { -			memset(oh->name, 0, sizeof(oh->name)); -		} +		if (!prioritised_exist)	/* None found, so we can clear this */ +			dev->has_pending_prioritised_gc = 0; +	} -		oh->isShrink = isShrink; +	/* If we're doing aggressive GC then we are happy to take a less-dirty +	 * block, and search harder. +	 * else (leasurely gc), then we only bother to do this if the +	 * block has only a few pages in use. +	 */ -		switch (in->variantType) { -		case YAFFS_OBJECT_TYPE_UNKNOWN: -			/* Should not happen */ -			break; -		case YAFFS_OBJECT_TYPE_FILE: -			oh->fileSize = -			    (oh->parentObjectId == YAFFS_OBJECTID_DELETED -			     || oh->parentObjectId == -			     YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant. -			    fileVariant.fileSize; -			break; -		case YAFFS_OBJECT_TYPE_HARDLINK: -			oh->equivalentObjectId = -			    in->variant.hardLinkVariant.equivalentObjectId; -			break; -		case YAFFS_OBJECT_TYPE_SPECIAL: -			/* Do nothing */ -			break; -		case YAFFS_OBJECT_TYPE_DIRECTORY: -			/* Do nothing */ -			break; -		case YAFFS_OBJECT_TYPE_SYMLINK: -			yaffs_strncpy(oh->alias, -				      in->variant.symLinkVariant.alias, -				      YAFFS_MAX_ALIAS_LENGTH); -			oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; -			break; -		} +	if (!selected) { +		int pages_used; +		int n_blocks = +		    dev->internal_end_block - dev->internal_start_block + 1; +		if (aggressive) { +			threshold = dev->param.chunks_per_block; +			iterations = n_blocks; +		} else { +			int max_threshold; -		/* Tags */ -		yaffs_InitialiseTags(&newTags); -		in->serial++; -		newTags.chunkId = 0; -		newTags.objectId = in->objectId; -		newTags.serialNumber = in->serial; +			if (background) +				max_threshold = dev->param.chunks_per_block / 2; +			else +				max_threshold = dev->param.chunks_per_block / 8; -		/* Add extra info for file header */ +			if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) +				max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; -		newTags.extraHeaderInfoAvailable = 1; -		newTags.extraParentObjectId = oh->parentObjectId; -		newTags.extraFileLength = oh->fileSize; -		newTags.extraIsShrinkHeader = oh->isShrink; -		newTags.extraEquivalentObjectId = oh->equivalentObjectId; -		newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0; -		newTags.extraObjectType = in->variantType; +			threshold = background ? (dev->gc_not_done + 2) * 2 : 0; +			if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) +				threshold = YAFFS_GC_PASSIVE_THRESHOLD; +			if (threshold > max_threshold) +				threshold = max_threshold; -		yaffs_VerifyObjectHeader(in,oh,&newTags,1); +			iterations = n_blocks / 16 + 1; +			if (iterations > 100) +				iterations = 100; +		} -		/* Create new chunk in NAND */ -		newChunkId = -		    yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, -						      (prevChunkId >= 0) ? 1 : 0); +		for (i = 0; +		     i < iterations && +		     (dev->gc_dirtiest < 1 || +		      dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); +		     i++) { +			dev->gc_block_finder++; +			if (dev->gc_block_finder < dev->internal_start_block || +			    dev->gc_block_finder > dev->internal_end_block) +				dev->gc_block_finder = +				    dev->internal_start_block; -		if (newChunkId >= 0) { +			bi = yaffs_get_block_info(dev, dev->gc_block_finder); -			in->chunkId = newChunkId; +			pages_used = bi->pages_in_use - bi->soft_del_pages; -			if (prevChunkId >= 0) { -				yaffs_DeleteChunk(dev, prevChunkId, 1, -						  __LINE__); +			if (bi->block_state == YAFFS_BLOCK_STATE_FULL && +			    pages_used < dev->param.chunks_per_block && +			    (dev->gc_dirtiest < 1 || +			     pages_used < dev->gc_pages_in_use) && +			    yaffs_block_ok_for_gc(dev, bi)) { +				dev->gc_dirtiest = dev->gc_block_finder; +				dev->gc_pages_in_use = pages_used;  			} +		} -			if(!yaffs_ObjectHasCachedWriteData(in)) -				in->dirty = 0; +		if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) +			selected = dev->gc_dirtiest; +	} -			/* If this was a shrink, then mark the block that the chunk lives on */ -			if (isShrink) { -				bi = yaffs_GetBlockInfo(in->myDev, -							newChunkId /in->myDev->	nChunksPerBlock); -				bi->hasShrinkHeader = 1; -			} +	/* +	 * If nothing has been selected for a while, try the oldest dirty +	 * because that's gumming up the works. +	 */ +	if (!selected && dev->param.is_yaffs2 && +	    dev->gc_not_done >= (background ? 10 : 20)) { +		yaffs2_find_oldest_dirty_seq(dev); +		if (dev->oldest_dirty_block > 0) { +			selected = dev->oldest_dirty_block; +			dev->gc_dirtiest = selected; +			dev->oldest_dirty_gc_count++; +			bi = yaffs_get_block_info(dev, selected); +			dev->gc_pages_in_use = +			    bi->pages_in_use - bi->soft_del_pages; +		} else { +			dev->gc_not_done = 0;  		} +	} -		retVal = newChunkId; +	if (selected) { +		yaffs_trace(YAFFS_TRACE_GC, +			"GC Selected block %d with %d free, prioritised:%d", +			selected, +			dev->param.chunks_per_block - dev->gc_pages_in_use, +			prioritised); -	} +		dev->n_gc_blocks++; +		if (background) +			dev->bg_gcs++; -	if (buffer) -		yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); +		dev->gc_dirtiest = 0; +		dev->gc_pages_in_use = 0; +		dev->gc_not_done = 0; +		if (dev->refresh_skip > 0) +			dev->refresh_skip--; +	} else { +		dev->gc_not_done++; +		yaffs_trace(YAFFS_TRACE_GC, +			"GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", +			dev->gc_block_finder, dev->gc_not_done, threshold, +			dev->gc_dirtiest, dev->gc_pages_in_use, +			dev->oldest_dirty_block, background ? " bg" : ""); +	} -	return retVal; +	return selected;  } -/*------------------------ Short Operations Cache ---------------------------------------- - *   In many situations where there is no high level buffering (eg WinCE) a lot of - *   reads might be short sequential reads, and a lot of writes may be short - *   sequential writes. eg. scanning/writing a jpeg file. - *   In these cases, a short read/write cache can provide a huge perfomance benefit - *   with dumb-as-a-rock code. - *   In Linux, the page cache provides read buffering aand the short op cache provides write - *   buffering. +/* New garbage collector + * If we're very low on erased blocks then we do aggressive garbage collection + * otherwise we do "leasurely" garbage collection. + * Aggressive gc looks further (whole array) and will accept less dirty blocks. + * Passive gc only inspects smaller areas and only accepts more dirty blocks.   * - *   There are a limited number (~10) of cache chunks per device so that we don't - *   need a very intelligent search. + * The idea is to help clear out space in a more spread-out manner. + * Dunno if it really does anything useful.   */ - -static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj) +static int yaffs_check_gc(struct yaffs_dev *dev, int background)  { -	yaffs_Device *dev = obj->myDev; -	int i; -	yaffs_ChunkCache *cache; -	int nCaches = obj->myDev->nShortOpCaches; +	int aggressive = 0; +	int gc_ok = YAFFS_OK; +	int max_tries = 0; +	int min_erased; +	int erased_chunks; +	int checkpt_block_adjust; -	for(i = 0; i < nCaches; i++){ -		cache = &dev->srCache[i]; -		if (cache->object == obj && -		    cache->dirty) -			return 1; -	} +	if (dev->param.gc_control && (dev->param.gc_control(dev) & 1) == 0) +		return YAFFS_OK; -	return 0; -} +	if (dev->gc_disable) +		/* Bail out so we don't get recursive gc */ +		return YAFFS_OK; +	/* This loop should pass the first time. +	 * Only loops here if the collection does not increase space. +	 */ -static void yaffs_FlushFilesChunkCache(yaffs_Object * obj) -{ -	yaffs_Device *dev = obj->myDev; -	int lowest = -99;	/* Stop compiler whining. */ -	int i; -	yaffs_ChunkCache *cache; -	int chunkWritten = 0; -	int nCaches = obj->myDev->nShortOpCaches; +	do { +		max_tries++; -	if (nCaches > 0) { -		do { -			cache = NULL; +		checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); -			/* Find the dirty cache for this object with the lowest chunk id. */ -			for (i = 0; i < nCaches; i++) { -				if (dev->srCache[i].object == obj && -				    dev->srCache[i].dirty) { -					if (!cache -					    || dev->srCache[i].chunkId < -					    lowest) { -						cache = &dev->srCache[i]; -						lowest = cache->chunkId; -					} -				} -			} +		min_erased = +		    dev->param.n_reserved_blocks + checkpt_block_adjust + 1; +		erased_chunks = +		    dev->n_erased_blocks * dev->param.chunks_per_block; -			if (cache && !cache->locked) { -				/* Write it out and free it up */ +		/* If we need a block soon then do aggressive gc. */ +		if (dev->n_erased_blocks < min_erased) +			aggressive = 1; +		else { +			if (!background +			    && erased_chunks > (dev->n_free_chunks / 4)) +				break; -				chunkWritten = -				    yaffs_WriteChunkDataToObject(cache->object, -								 cache->chunkId, -								 cache->data, -								 cache->nBytes, -								 1); -				cache->dirty = 0; -				cache->object = NULL; +			if (dev->gc_skip > 20) +				dev->gc_skip = 20; +			if (erased_chunks < dev->n_free_chunks / 2 || +			    dev->gc_skip < 1 || background) +				aggressive = 0; +			else { +				dev->gc_skip--; +				break;  			} +		} -		} while (cache && chunkWritten > 0); +		dev->gc_skip = 5; -		if (cache) { -			/* Hoosterman, disk full while writing cache out. */ -			T(YAFFS_TRACE_ERROR, -			  (TSTR("yaffs tragedy: no space during cache write" TENDSTR))); +		/* If we don't already have a block being gc'd then see if we +		 * should start another */ +		if (dev->gc_block < 1 && !aggressive) { +			dev->gc_block = yaffs2_find_refresh_block(dev); +			dev->gc_chunk = 0; +			dev->n_clean_ups = 0; +		} +		if (dev->gc_block < 1) { +			dev->gc_block = +			    yaffs_find_gc_block(dev, aggressive, background); +			dev->gc_chunk = 0; +			dev->n_clean_ups = 0;  		} -	} - -} - -/*yaffs_FlushEntireDeviceCache(dev) - * - * - */ -void yaffs_FlushEntireDeviceCache(yaffs_Device *dev) -{ -	yaffs_Object *obj; -	int nCaches = dev->nShortOpCaches; -	int i; +		if (dev->gc_block > 0) { +			dev->all_gcs++; +			if (!aggressive) +				dev->passive_gc_count++; -	/* Find a dirty object in the cache and flush it... -	 * until there are no further dirty objects. -	 */ -	do { -		obj = NULL; -		for( i = 0; i < nCaches && !obj; i++) { -			if (dev->srCache[i].object && -			    dev->srCache[i].dirty) -				obj = dev->srCache[i].object; +			yaffs_trace(YAFFS_TRACE_GC, +				"yaffs: GC n_erased_blocks %d aggressive %d", +				dev->n_erased_blocks, aggressive); +			gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive);  		} -		if(obj) -			yaffs_FlushFilesChunkCache(obj); -	} while(obj); +		if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && +		    dev->gc_block > 0) { +			yaffs_trace(YAFFS_TRACE_GC, +				"yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", +				dev->n_erased_blocks, max_tries, +				dev->gc_block); +		} +	} while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && +		 (dev->gc_block > 0) && (max_tries < 2)); +	return aggressive ? gc_ok : YAFFS_OK;  } - -/* Grab us a cache chunk for use. - * First look for an empty one. - * Then look for the least recently used non-dirty one. - * Then look for the least recently used dirty one...., flush and look again. +/* + * yaffs_bg_gc() + * Garbage collects. Intended to be called from a background thread. + * Returns non-zero if at least half the free chunks are erased.   */ -static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device * dev) +int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency)  { -	int i; -	int usage; -	int theOne; +	int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; -	if (dev->nShortOpCaches > 0) { -		for (i = 0; i < dev->nShortOpCaches; i++) { -			if (!dev->srCache[i].object) -				return &dev->srCache[i]; -		} +	yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); -		return NULL; - -		theOne = -1; -		usage = 0;	/* just to stop the compiler grizzling */ +	yaffs_check_gc(dev, 1); +	return erased_chunks > dev->n_free_chunks / 2; +} -		for (i = 0; i < dev->nShortOpCaches; i++) { -			if (!dev->srCache[i].dirty && -			    ((dev->srCache[i].lastUse < usage && theOne >= 0) || -			     theOne < 0)) { -				usage = dev->srCache[i].lastUse; -				theOne = i; -			} -		} +/*-------------------- Data file manipulation -----------------*/ +static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) +{ +	int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); -		return theOne >= 0 ? &dev->srCache[theOne] : NULL; -	} else { -		return NULL; +	if (nand_chunk >= 0) +		return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, +						buffer, NULL); +	else { +		yaffs_trace(YAFFS_TRACE_NANDACCESS, +			"Chunk %d not found zero instead", +			nand_chunk); +		/* get sane (zero) data if you read a hole */ +		memset(buffer, 0, in->my_dev->data_bytes_per_chunk); +		return 0;  	}  } -static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device * dev) +void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, +		     int lyn)  { -	yaffs_ChunkCache *cache; -	yaffs_Object *theObj; -	int usage; -	int i; +	int block; +	int page; +	struct yaffs_ext_tags tags; +	struct yaffs_block_info *bi; -	if (dev->nShortOpCaches > 0) { -		/* Try find a non-dirty one... */ +	if (chunk_id <= 0) +		return; -		cache = yaffs_GrabChunkCacheWorker(dev); +	dev->n_deletions++; +	block = chunk_id / dev->param.chunks_per_block; +	page = chunk_id % dev->param.chunks_per_block; -		if (!cache) { -			/* They were all dirty, find the last recently used object and flush -			 * its cache, then  find again. -			 * NB what's here is not very accurate, we actually flush the object -			 * the last recently used page. -			 */ +	if (!yaffs_check_chunk_bit(dev, block, page)) +		yaffs_trace(YAFFS_TRACE_VERIFY, +			"Deleting invalid chunk %d", chunk_id); -			/* With locking we can't assume we can use entry zero */ +	bi = yaffs_get_block_info(dev, block); -			theObj = NULL; -			usage = -1; -			cache = NULL; +	yaffs2_update_oldest_dirty_seq(dev, block, bi); -			for (i = 0; i < dev->nShortOpCaches; i++) { -				if (dev->srCache[i].object && -				    !dev->srCache[i].locked && -				    (dev->srCache[i].lastUse < usage || !cache)) -				{ -					usage = dev->srCache[i].lastUse; -					theObj = dev->srCache[i].object; -					cache = &dev->srCache[i]; -				} -			} - -			if (!cache || cache->dirty) { -				/* Flush and try again */ -				yaffs_FlushFilesChunkCache(theObj); -				cache = yaffs_GrabChunkCacheWorker(dev); -			} +	yaffs_trace(YAFFS_TRACE_DELETION, +		"line %d delete of chunk %d", +		lyn, chunk_id); -		} -		return cache; -	} else -		return NULL; +	if (!dev->param.is_yaffs2 && mark_flash && +	    bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { -} +		memset(&tags, 0, sizeof(tags)); +		tags.is_deleted = 1; +		yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); +		yaffs_handle_chunk_update(dev, chunk_id, &tags); +	} else { +		dev->n_unmarked_deletions++; +	} -/* Find a cached chunk */ -static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object * obj, -					      int chunkId) -{ -	yaffs_Device *dev = obj->myDev; -	int i; -	if (dev->nShortOpCaches > 0) { -		for (i = 0; i < dev->nShortOpCaches; i++) { -			if (dev->srCache[i].object == obj && -			    dev->srCache[i].chunkId == chunkId) { -				dev->cacheHits++; +	/* Pull out of the management area. +	 * If the whole block became dirty, this will kick off an erasure. +	 */ +	if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || +	    bi->block_state == YAFFS_BLOCK_STATE_FULL || +	    bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || +	    bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { +		dev->n_free_chunks++; +		yaffs_clear_chunk_bit(dev, block, page); +		bi->pages_in_use--; -				return &dev->srCache[i]; -			} +		if (bi->pages_in_use == 0 && +		    !bi->has_shrink_hdr && +		    bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && +		    bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { +			yaffs_block_became_dirty(dev, block);  		}  	} -	return NULL;  } -/* Mark the chunk for the least recently used algorithym */ -static void yaffs_UseChunkCache(yaffs_Device * dev, yaffs_ChunkCache * cache, -				int isAWrite) +static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, +			     const u8 *buffer, int n_bytes, int use_reserve)  { +	/* Find old chunk Need to do this to get serial number +	 * Write new one and patch into tree. +	 * Invalidate old tags. +	 */ -	if (dev->nShortOpCaches > 0) { -		if (dev->srLastUse < 0 || dev->srLastUse > 100000000) { -			/* Reset the cache usages */ -			int i; -			for (i = 1; i < dev->nShortOpCaches; i++) { -				dev->srCache[i].lastUse = 0; -			} -			dev->srLastUse = 0; -		} - -		dev->srLastUse++; - -		cache->lastUse = dev->srLastUse; +	int prev_chunk_id; +	struct yaffs_ext_tags prev_tags; +	int new_chunk_id; +	struct yaffs_ext_tags new_tags; +	struct yaffs_dev *dev = in->my_dev; -		if (isAWrite) { -			cache->dirty = 1; -		} -	} -} +	yaffs_check_gc(dev, 0); -/* Invalidate a single cache page. - * Do this when a whole page gets written, - * ie the short cache for this page is no longer valid. - */ -static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId) -{ -	if (object->myDev->nShortOpCaches > 0) { -		yaffs_ChunkCache *cache = yaffs_FindChunkCache(object, chunkId); +	/* Get the previous chunk at this location in the file if it exists. +	 * If it does not exist then put a zero into the tree. This creates +	 * the tnode now, rather than later when it is harder to clean up. +	 */ +	prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); +	if (prev_chunk_id < 1 && +	    !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) +		return 0; -		if (cache) { -			cache->object = NULL; -		} -	} -} +	/* Set up new tags */ +	memset(&new_tags, 0, sizeof(new_tags)); -/* Invalidate all the cache pages associated with this object - * Do this whenever ther file is deleted or resized. - */ -static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in) -{ -	int i; -	yaffs_Device *dev = in->myDev; +	new_tags.chunk_id = inode_chunk; +	new_tags.obj_id = in->obj_id; +	new_tags.serial_number = +	    (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; +	new_tags.n_bytes = n_bytes; -	if (dev->nShortOpCaches > 0) { -		/* Invalidate it. */ -		for (i = 0; i < dev->nShortOpCaches; i++) { -			if (dev->srCache[i].object == in) { -				dev->srCache[i].object = NULL; -			} -		} +	if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { +		yaffs_trace(YAFFS_TRACE_ERROR, +		  "Writing %d bytes to chunk!!!!!!!!!", +		   n_bytes); +		BUG();  	} -} -/*--------------------- Checkpointing --------------------*/ +	new_chunk_id = +	    yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); +	if (new_chunk_id > 0) { +		yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); -static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head) -{ -	yaffs_CheckpointValidity cp; +		if (prev_chunk_id > 0) +			yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -	memset(&cp,0,sizeof(cp)); - -	cp.structType = sizeof(cp); -	cp.magic = YAFFS_MAGIC; -	cp.version = YAFFS_CHECKPOINT_VERSION; -	cp.head = (head) ? 1 : 0; +		yaffs_verify_file_sane(in); +	} +	return new_chunk_id; -	return (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp))? -		1 : 0;  } -static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head) -{ -	yaffs_CheckpointValidity cp; -	int ok; - -	ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); -	if(ok) -		ok = (cp.structType == sizeof(cp)) && -		     (cp.magic == YAFFS_MAGIC) && -		     (cp.version == YAFFS_CHECKPOINT_VERSION) && -		     (cp.head == ((head) ? 1 : 0)); -	return ok ? 1 : 0; -} -static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, -					   yaffs_Device *dev) +static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, +				const YCHAR *name, const void *value, int size, +				int flags)  { -	cp->nErasedBlocks = dev->nErasedBlocks; -	cp->allocationBlock = dev->allocationBlock; -	cp->allocationPage = dev->allocationPage; -	cp->nFreeChunks = dev->nFreeChunks; +	struct yaffs_xattr_mod xmod; +	int result; -	cp->nDeletedFiles = dev->nDeletedFiles; -	cp->nUnlinkedFiles = dev->nUnlinkedFiles; -	cp->nBackgroundDeletions = dev->nBackgroundDeletions; -	cp->sequenceNumber = dev->sequenceNumber; -	cp->oldestDirtySequence = dev->oldestDirtySequence; +	xmod.set = set; +	xmod.name = name; +	xmod.data = value; +	xmod.size = size; +	xmod.flags = flags; +	xmod.result = -ENOSPC; -} +	result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); -static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev, -					   yaffs_CheckpointDevice *cp) -{ -	dev->nErasedBlocks = cp->nErasedBlocks; -	dev->allocationBlock = cp->allocationBlock; -	dev->allocationPage = cp->allocationPage; -	dev->nFreeChunks = cp->nFreeChunks; - -	dev->nDeletedFiles = cp->nDeletedFiles; -	dev->nUnlinkedFiles = cp->nUnlinkedFiles; -	dev->nBackgroundDeletions = cp->nBackgroundDeletions; -	dev->sequenceNumber = cp->sequenceNumber; -	dev->oldestDirtySequence = cp->oldestDirtySequence; +	if (result > 0) +		return xmod.result; +	else +		return -ENOSPC;  } - -static int yaffs_WriteCheckpointDevice(yaffs_Device *dev) +static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, +				   struct yaffs_xattr_mod *xmod)  { -	yaffs_CheckpointDevice cp; -	__u32 nBytes; -	__u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); +	int retval = 0; +	int x_offs = sizeof(struct yaffs_obj_hdr); +	struct yaffs_dev *dev = obj->my_dev; +	int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); +	char *x_buffer = buffer + x_offs; -	int ok; - -	/* Write device runtime values*/ -	yaffs_DeviceToCheckpointDevice(&cp,dev); -	cp.structType = sizeof(cp); - -	ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); - -	/* Write block info */ -	if(ok) { -		nBytes = nBlocks * sizeof(yaffs_BlockInfo); -		ok = (yaffs_CheckpointWrite(dev,dev->blockInfo,nBytes) == nBytes); -	} +	if (xmod->set) +		retval = +		    nval_set(x_buffer, x_size, xmod->name, xmod->data, +			     xmod->size, xmod->flags); +	else +		retval = nval_del(x_buffer, x_size, xmod->name); -	/* Write chunk bits */ -	if(ok) { -		nBytes = nBlocks * dev->chunkBitmapStride; -		ok = (yaffs_CheckpointWrite(dev,dev->chunkBits,nBytes) == nBytes); -	} -	return	 ok ? 1 : 0; +	obj->has_xattr = nval_hasvalues(x_buffer, x_size); +	obj->xattr_known = 1; +	xmod->result = retval; +	return retval;  } -static int yaffs_ReadCheckpointDevice(yaffs_Device *dev) +static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, +				  void *value, int size)  { -	yaffs_CheckpointDevice cp; -	__u32 nBytes; -	__u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); +	char *buffer = NULL; +	int result; +	struct yaffs_ext_tags tags; +	struct yaffs_dev *dev = obj->my_dev; +	int x_offs = sizeof(struct yaffs_obj_hdr); +	int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); +	char *x_buffer; +	int retval = 0; -	int ok; - -	ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); -	if(!ok) -		return 0; +	if (obj->hdr_chunk < 1) +		return -ENODATA; -	if(cp.structType != sizeof(cp)) -		return 0; - - -	yaffs_CheckpointDeviceToDevice(dev,&cp); +	/* If we know that the object has no xattribs then don't do all the +	 * reading and parsing. +	 */ +	if (obj->xattr_known && !obj->has_xattr) { +		if (name) +			return -ENODATA; +		else +			return 0; +	} -	nBytes = nBlocks * sizeof(yaffs_BlockInfo); +	buffer = (char *)yaffs_get_temp_buffer(dev); +	if (!buffer) +		return -ENOMEM; -	ok = (yaffs_CheckpointRead(dev,dev->blockInfo,nBytes) == nBytes); +	result = +	    yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); -	if(!ok) -		return 0; -	nBytes = nBlocks * dev->chunkBitmapStride; +	if (result != YAFFS_OK) +		retval = -ENOENT; +	else { +		x_buffer = buffer + x_offs; -	ok = (yaffs_CheckpointRead(dev,dev->chunkBits,nBytes) == nBytes); +		if (!obj->xattr_known) { +			obj->has_xattr = nval_hasvalues(x_buffer, x_size); +			obj->xattr_known = 1; +		} -	return ok ? 1 : 0; +		if (name) +			retval = nval_get(x_buffer, x_size, name, value, size); +		else +			retval = nval_list(x_buffer, x_size, value, size); +	} +	yaffs_release_temp_buffer(dev, (u8 *) buffer); +	return retval;  } -static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject *cp, -					   yaffs_Object *obj) +int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, +		      const void *value, int size, int flags)  { - -	cp->objectId = obj->objectId; -	cp->parentId = (obj->parent) ? obj->parent->objectId : 0; -	cp->chunkId = obj->chunkId; -	cp->variantType = obj->variantType; -	cp->deleted = obj->deleted; -	cp->softDeleted = obj->softDeleted; -	cp->unlinked = obj->unlinked; -	cp->fake = obj->fake; -	cp->renameAllowed = obj->renameAllowed; -	cp->unlinkAllowed = obj->unlinkAllowed; -	cp->serial = obj->serial; -	cp->nDataChunks = obj->nDataChunks; - -	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) -		cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize; -	else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) -		cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId; +	return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags);  } -static void yaffs_CheckpointObjectToObject( yaffs_Object *obj,yaffs_CheckpointObject *cp) +int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name)  { - -	yaffs_Object *parent; - -	obj->objectId = cp->objectId; - -	if(cp->parentId) -		parent = yaffs_FindOrCreateObjectByNumber( -					obj->myDev, -					cp->parentId, -					YAFFS_OBJECT_TYPE_DIRECTORY); -	else -		parent = NULL; - -	if(parent) -		yaffs_AddObjectToDirectory(parent, obj); - -	obj->chunkId = cp->chunkId; -	obj->variantType = cp->variantType; -	obj->deleted = cp->deleted; -	obj->softDeleted = cp->softDeleted; -	obj->unlinked = cp->unlinked; -	obj->fake = cp->fake; -	obj->renameAllowed = cp->renameAllowed; -	obj->unlinkAllowed = cp->unlinkAllowed; -	obj->serial = cp->serial; -	obj->nDataChunks = cp->nDataChunks; - -	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) -		obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId; -	else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) -		obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId; - -	if(obj->objectId >= YAFFS_NOBJECT_BUCKETS) -		obj->lazyLoaded = 1; +	return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0);  } - - -static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn, -					__u32 level, int chunkOffset) +int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, +		      int size)  { -	int i; -	yaffs_Device *dev = in->myDev; -	int ok = 1; -	int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; - -	if (tn) { -		if (level > 0) { - -			for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){ -				if (tn->internal[i]) { -					ok = yaffs_CheckpointTnodeWorker(in, -							tn->internal[i], -							level - 1, -							(chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i); -				} -			} -		} else if (level == 0) { -			__u32 baseOffset = chunkOffset <<  YAFFS_TNODES_LEVEL0_BITS; -			/* printf("write tnode at %d\n",baseOffset); */ -			ok = (yaffs_CheckpointWrite(dev,&baseOffset,sizeof(baseOffset)) == sizeof(baseOffset)); -			if(ok) -				ok = (yaffs_CheckpointWrite(dev,tn,nTnodeBytes) == nTnodeBytes); -		} -	} - -	return ok; - +	return yaffs_do_xattrib_fetch(obj, name, value, size);  } -static int yaffs_WriteCheckpointTnodes(yaffs_Object *obj) +int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size)  { -	__u32 endMarker = ~0; -	int ok = 1; - -	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE){ -		ok = yaffs_CheckpointTnodeWorker(obj, -					    obj->variant.fileVariant.top, -					    obj->variant.fileVariant.topLevel, -					    0); -		if(ok) -			ok = (yaffs_CheckpointWrite(obj->myDev,&endMarker,sizeof(endMarker)) == -				sizeof(endMarker)); -	} - -	return ok ? 1 : 0; +	return yaffs_do_xattrib_fetch(obj, NULL, buffer, size);  } -static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) +static void yaffs_check_obj_details_loaded(struct yaffs_obj *in)  { -	__u32 baseChunk; -	int ok = 1; -	yaffs_Device *dev = obj->myDev; -	yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant; -	yaffs_Tnode *tn; -	int nread = 0; - -	ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); +	u8 *buf; +	struct yaffs_obj_hdr *oh; +	struct yaffs_dev *dev; +	struct yaffs_ext_tags tags; +	int result; +	int alloc_failed = 0; -	while(ok && (~baseChunk)){ -		nread++; -		/* Read level 0 tnode */ +	if (!in || !in->lazy_loaded || in->hdr_chunk < 1) +		return; +	dev = in->my_dev; +	in->lazy_loaded = 0; +	buf = yaffs_get_temp_buffer(dev); -		/* printf("read  tnode at %d\n",baseChunk); */ -		tn = yaffs_GetTnodeRaw(dev); -		if(tn) -			ok = (yaffs_CheckpointRead(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8) == -			      (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); -		else -			ok = 0; +	result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); +	oh = (struct yaffs_obj_hdr *)buf; -		if(tn && ok){ -			ok = yaffs_AddOrFindLevel0Tnode(dev, -							fileStructPtr, -							baseChunk, -							tn) ? 1 : 0; - -		} - -		if(ok) -			ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); +	in->yst_mode = oh->yst_mode; +	yaffs_load_attribs(in, oh); +	yaffs_set_obj_name_from_oh(in, oh); +	if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { +		in->variant.symlink_variant.alias = +		    yaffs_clone_str(oh->alias); +		if (!in->variant.symlink_variant.alias) +			alloc_failed = 1;	/* Not returned */  	} - -	T(YAFFS_TRACE_CHECKPOINT,( -		TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR), -		nread,baseChunk,ok)); - -	return ok ? 1 : 0; +	yaffs_release_temp_buffer(dev, buf);  } - -static int yaffs_WriteCheckpointObjects(yaffs_Device *dev) +static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, +				    const YCHAR *oh_name, int buff_size)  { -	yaffs_Object *obj; -	yaffs_CheckpointObject cp; -	int i; -	int ok = 1; -	struct list_head *lh; - - -	/* Iterate through the objects in each hash entry, -	 * dumping them to the checkpointing stream. -	 */ - -	 for(i = 0; ok &&  i <  YAFFS_NOBJECT_BUCKETS; i++){ -		list_for_each(lh, &dev->objectBucket[i].list) { -			if (lh) { -				obj = list_entry(lh, yaffs_Object, hashLink); -				if (!obj->deferedFree) { -					yaffs_ObjectToCheckpointObject(&cp,obj); -					cp.structType = sizeof(cp); - -					T(YAFFS_TRACE_CHECKPOINT,( -						TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR), -						cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj)); - -					ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); - -					if(ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE){ -						ok = yaffs_WriteCheckpointTnodes(obj); -					} -				} +#ifdef CONFIG_YAFFS_AUTO_UNICODE +	if (dev->param.auto_unicode) { +		if (*oh_name) { +			/* It is an ASCII name, do an ASCII to +			 * unicode conversion */ +			const char *ascii_oh_name = (const char *)oh_name; +			int n = buff_size - 1; +			while (n > 0 && *ascii_oh_name) { +				*name = *ascii_oh_name; +				name++; +				ascii_oh_name++; +				n--;  			} +		} else { +			yaffs_strncpy(name, oh_name + 1, buff_size - 1);  		} -	 } - -	 /* Dump end of list */ -	memset(&cp,0xFF,sizeof(yaffs_CheckpointObject)); -	cp.structType = sizeof(cp); - -	if(ok) -		ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); - -	return ok ? 1 : 0; +	} else { +#else +	dev = dev; +	{ +#endif +		yaffs_strncpy(name, oh_name, buff_size - 1); +	}  } -static int yaffs_ReadCheckpointObjects(yaffs_Device *dev) +static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, +				    const YCHAR *name)  { -	yaffs_Object *obj; -	yaffs_CheckpointObject cp; -	int ok = 1; -	int done = 0; -	yaffs_Object *hardList = NULL; +#ifdef CONFIG_YAFFS_AUTO_UNICODE -	while(ok && !done) { -		ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); -		if(cp.structType != sizeof(cp)) { -			T(YAFFS_TRACE_CHECKPOINT,(TSTR("struct size %d instead of %d ok %d"TENDSTR), -				cp.structType,sizeof(cp),ok)); -			ok = 0; -		} +	int is_ascii; +	YCHAR *w; -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("Checkpoint read object %d parent %d type %d chunk %d " TENDSTR), -			cp.objectId,cp.parentId,cp.variantType,cp.chunkId)); +	if (dev->param.auto_unicode) { -		if(ok && cp.objectId == ~0) -			done = 1; -		else if(ok){ -			obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType); -			if(obj) { -				yaffs_CheckpointObjectToObject(obj,&cp); -				if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) { -					ok = yaffs_ReadCheckpointTnodes(obj); -				} else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { -					obj->hardLinks.next = -						    (struct list_head *) -						    hardList; -					hardList = obj; -				} +		is_ascii = 1; +		w = name; + +		/* Figure out if the name will fit in ascii character set */ +		while (is_ascii && *w) { +			if ((*w) & 0xff00) +				is_ascii = 0; +			w++; +		} +		if (is_ascii) { +			/* It is an ASCII name, so convert unicode to ascii */ +			char *ascii_oh_name = (char *)oh_name; +			int n = YAFFS_MAX_NAME_LENGTH - 1; +			while (n > 0 && *name) { +				*ascii_oh_name = *name; +				name++; +				ascii_oh_name++; +				n--;  			} +		} else { +			/* Unicode name, so save starting at the second YCHAR */ +			*oh_name = 0; +			yaffs_strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2);  		} +	} else { +#else +	dev = dev; +	{ +#endif +		yaffs_strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1);  	} - -	if(ok) -		yaffs_HardlinkFixup(dev,hardList); - -	return ok ? 1 : 0;  } -static int yaffs_WriteCheckpointSum(yaffs_Device *dev) -{ -	__u32 checkpointSum; -	int ok; - -	yaffs_GetCheckpointSum(dev,&checkpointSum); - -	ok = (yaffs_CheckpointWrite(dev,&checkpointSum,sizeof(checkpointSum)) == sizeof(checkpointSum)); - -	if(!ok) -		return 0; - -	return 1; -} - -static int yaffs_ReadCheckpointSum(yaffs_Device *dev) +/* UpdateObjectHeader updates the header on NAND for an object. + * If name is not NULL, then that new name is used. + */ +int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, +		    int is_shrink, int shadows, struct yaffs_xattr_mod *xmod)  { -	__u32 checkpointSum0; -	__u32 checkpointSum1; -	int ok; - -	yaffs_GetCheckpointSum(dev,&checkpointSum0); -	ok = (yaffs_CheckpointRead(dev,&checkpointSum1,sizeof(checkpointSum1)) == sizeof(checkpointSum1)); +	struct yaffs_block_info *bi; +	struct yaffs_dev *dev = in->my_dev; +	int prev_chunk_id; +	int ret_val = 0; +	int result = 0; +	int new_chunk_id; +	struct yaffs_ext_tags new_tags; +	struct yaffs_ext_tags old_tags; +	const YCHAR *alias = NULL; +	u8 *buffer = NULL; +	YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; +	struct yaffs_obj_hdr *oh = NULL; +	loff_t file_size = 0; -	if(!ok) -		return 0; +	yaffs_strcpy(old_name, _Y("silly old name")); -	if(checkpointSum0 != checkpointSum1) -		return 0; +	if (in->fake && in != dev->root_dir && !force && !xmod) +		return ret_val; -	return 1; -} +	yaffs_check_gc(dev, 0); +	yaffs_check_obj_details_loaded(in); +	buffer = yaffs_get_temp_buffer(in->my_dev); +	oh = (struct yaffs_obj_hdr *)buffer; -static int yaffs_WriteCheckpointData(yaffs_Device *dev) -{ +	prev_chunk_id = in->hdr_chunk; -	int ok = 1; +	if (prev_chunk_id > 0) { +		result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, +						  buffer, &old_tags); -	if(dev->skipCheckpointWrite || !dev->isYaffs2){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint write" TENDSTR))); -		ok = 0; -	} - -	if(ok) -		ok = yaffs_CheckpointOpen(dev,1); - -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); -		ok = yaffs_WriteCheckpointValidityMarker(dev,1); -	} -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint device" TENDSTR))); -		ok = yaffs_WriteCheckpointDevice(dev); -	} -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint objects" TENDSTR))); -		ok = yaffs_WriteCheckpointObjects(dev); -	} -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); -		ok = yaffs_WriteCheckpointValidityMarker(dev,0); -	} - -	if(ok){ -		ok = yaffs_WriteCheckpointSum(dev); +		yaffs_verify_oh(in, oh, &old_tags, 0); +		memcpy(old_name, oh->name, sizeof(oh->name)); +		memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); +	} else { +		memset(buffer, 0xff, dev->data_bytes_per_chunk);  	} +	oh->type = in->variant_type; +	oh->yst_mode = in->yst_mode; +	oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; -	if(!yaffs_CheckpointClose(dev)) -		 ok = 0; - -	if(ok) -		dev->isCheckpointed = 1; -	 else -		dev->isCheckpointed = 0; +	yaffs_load_attribs_oh(oh, in); -	return dev->isCheckpointed; -} - -static int yaffs_ReadCheckpointData(yaffs_Device *dev) -{ -	int ok = 1; - -	if(dev->skipCheckpointRead || !dev->isYaffs2){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint read" TENDSTR))); -		ok = 0; -	} - -	if(ok) -		ok = yaffs_CheckpointOpen(dev,0); /* open for read */ - -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); -		ok = yaffs_ReadCheckpointValidityMarker(dev,1); -	} -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint device" TENDSTR))); -		ok = yaffs_ReadCheckpointDevice(dev); -	} -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint objects" TENDSTR))); -		ok = yaffs_ReadCheckpointObjects(dev); -	} -	if(ok){ -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); -		ok = yaffs_ReadCheckpointValidityMarker(dev,0); -	} +	if (in->parent) +		oh->parent_obj_id = in->parent->obj_id; +	else +		oh->parent_obj_id = 0; -	if(ok){ -		ok = yaffs_ReadCheckpointSum(dev); -		T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint checksum %d" TENDSTR),ok)); +	if (name && *name) { +		memset(oh->name, 0, sizeof(oh->name)); +		yaffs_load_oh_from_name(dev, oh->name, name); +	} else if (prev_chunk_id > 0) { +		memcpy(oh->name, old_name, sizeof(oh->name)); +	} else { +		memset(oh->name, 0, sizeof(oh->name));  	} -	if(!yaffs_CheckpointClose(dev)) -		ok = 0; - -	if(ok) -		dev->isCheckpointed = 1; -	 else -		dev->isCheckpointed = 0; +	oh->is_shrink = is_shrink; -	return ok ? 1 : 0; - -} - -static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) -{ -	if(dev->isCheckpointed || -	   dev->blocksInCheckpoint > 0){ -		dev->isCheckpointed = 0; -		yaffs_CheckpointInvalidateStream(dev); -		if(dev->superBlock && dev->markSuperBlockDirty) -			dev->markSuperBlockDirty(dev->superBlock); +	switch (in->variant_type) { +	case YAFFS_OBJECT_TYPE_UNKNOWN: +		/* Should not happen */ +		break; +	case YAFFS_OBJECT_TYPE_FILE: +		if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && +		    oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) +			file_size = in->variant.file_variant.file_size; +		yaffs_oh_size_load(oh, file_size); +		break; +	case YAFFS_OBJECT_TYPE_HARDLINK: +		oh->equiv_id = in->variant.hardlink_variant.equiv_id; +		break; +	case YAFFS_OBJECT_TYPE_SPECIAL: +		/* Do nothing */ +		break; +	case YAFFS_OBJECT_TYPE_DIRECTORY: +		/* Do nothing */ +		break; +	case YAFFS_OBJECT_TYPE_SYMLINK: +		alias = in->variant.symlink_variant.alias; +		if (!alias) +			alias = _Y("no alias"); +		yaffs_strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); +		oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; +		break;  	} -} +	/* process any xattrib modifications */ +	if (xmod) +		yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); -int yaffs_CheckpointSave(yaffs_Device *dev) -{ +	/* Tags */ +	memset(&new_tags, 0, sizeof(new_tags)); +	in->serial++; +	new_tags.chunk_id = 0; +	new_tags.obj_id = in->obj_id; +	new_tags.serial_number = in->serial; -	T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); +	/* Add extra info for file header */ +	new_tags.extra_available = 1; +	new_tags.extra_parent_id = oh->parent_obj_id; +	new_tags.extra_file_size = file_size; +	new_tags.extra_is_shrink = oh->is_shrink; +	new_tags.extra_equiv_id = oh->equiv_id; +	new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; +	new_tags.extra_obj_type = in->variant_type; +	yaffs_verify_oh(in, oh, &new_tags, 1); -	yaffs_VerifyObjects(dev); -	yaffs_VerifyBlocks(dev); -	yaffs_VerifyFreeChunks(dev); +	/* Create new chunk in NAND */ +	new_chunk_id = +	    yaffs_write_new_chunk(dev, buffer, &new_tags, +				  (prev_chunk_id > 0) ? 1 : 0); -	if(!dev->isCheckpointed) { -		yaffs_InvalidateCheckpoint(dev); -		yaffs_WriteCheckpointData(dev); -	} +	if (buffer) +		yaffs_release_temp_buffer(dev, buffer); -	T(YAFFS_TRACE_ALWAYS,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); +	if (new_chunk_id < 0) +		return new_chunk_id; -	return dev->isCheckpointed; -} +	in->hdr_chunk = new_chunk_id; -int yaffs_CheckpointRestore(yaffs_Device *dev) -{ -	int retval; -	T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); +	if (prev_chunk_id > 0) +		yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -	retval = yaffs_ReadCheckpointData(dev); +	if (!yaffs_obj_cache_dirty(in)) +		in->dirty = 0; -	if(dev->isCheckpointed){ -		yaffs_VerifyObjects(dev); -		yaffs_VerifyBlocks(dev); -		yaffs_VerifyFreeChunks(dev); +	/* If this was a shrink, then mark the block +	 * that the chunk lives on */ +	if (is_shrink) { +		bi = yaffs_get_block_info(in->my_dev, +					  new_chunk_id / +					  in->my_dev->param.chunks_per_block); +		bi->has_shrink_hdr = 1;  	} -	T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); -	return retval; +	return new_chunk_id;  }  /*--------------------- File read/write ------------------------ @@ -4447,622 +3432,550 @@ int yaffs_CheckpointRestore(yaffs_Device *dev)   * Curve-balls: the first chunk might also be the last chunk.   */ -int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset, -			   int nBytes) +int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes)  { +	int chunk; +	u32 start; +	int n_copy; +	int n = n_bytes; +	int n_done = 0; +	struct yaffs_cache *cache; +	struct yaffs_dev *dev; -	__u32 chunk = 0; -	__u32 start = 0; -	int nToCopy; -	int n = nBytes; -	int nDone = 0; -	yaffs_ChunkCache *cache; - -	yaffs_Device *dev; - -	dev = in->myDev; +	dev = in->my_dev;  	while (n > 0) { -		//chunk = offset / dev->nDataBytesPerChunk + 1; -		//start = offset % dev->nDataBytesPerChunk; -		yaffs_AddrToChunk(dev,offset,&chunk,&start); +		yaffs_addr_to_chunk(dev, offset, &chunk, &start);  		chunk++;  		/* OK now check for the curveball where the start and end are in  		 * the same chunk.  		 */ -		if ((start + n) < dev->nDataBytesPerChunk) { -			nToCopy = n; -		} else { -			nToCopy = dev->nDataBytesPerChunk - start; -		} +		if ((start + n) < dev->data_bytes_per_chunk) +			n_copy = n; +		else +			n_copy = dev->data_bytes_per_chunk - start; -		cache = yaffs_FindChunkCache(in, chunk); +		cache = yaffs_find_chunk_cache(in, chunk); -		/* If the chunk is already in the cache or it is less than a whole chunk -		 * then use the cache (if there is caching) -		 * else bypass the cache. +		/* If the chunk is already in the cache or it is less than +		 * a whole chunk or we're using inband tags then use the cache +		 * (if there is caching) else bypass the cache.  		 */ -		if (cache || nToCopy != dev->nDataBytesPerChunk) { -			if (dev->nShortOpCaches > 0) { +		if (cache || n_copy != dev->data_bytes_per_chunk || +		    dev->param.inband_tags) { +			if (dev->param.n_caches > 0) { -				/* If we can't find the data in the cache, then load it up. */ +				/* If we can't find the data in the cache, +				 * then load it up. */  				if (!cache) { -					cache = yaffs_GrabChunkCache(in->myDev); +					cache = +					    yaffs_grab_chunk_cache(in->my_dev);  					cache->object = in; -					cache->chunkId = chunk; +					cache->chunk_id = chunk;  					cache->dirty = 0;  					cache->locked = 0; -					yaffs_ReadChunkDataFromObject(in, chunk, -								      cache-> -								      data); -					cache->nBytes = 0; +					yaffs_rd_data_obj(in, chunk, +							  cache->data); +					cache->n_bytes = 0;  				} -				yaffs_UseChunkCache(dev, cache, 0); +				yaffs_use_cache(dev, cache, 0);  				cache->locked = 1; -#ifdef CONFIG_YAFFS_WINCE -				yfsd_UnlockYAFFS(TRUE); -#endif -				memcpy(buffer, &cache->data[start], nToCopy); +				memcpy(buffer, &cache->data[start], n_copy); -#ifdef CONFIG_YAFFS_WINCE -				yfsd_LockYAFFS(TRUE); -#endif  				cache->locked = 0;  			} else { -				/* Read into the local buffer then copy..*/ +				/* Read into the local buffer then copy.. */ -				__u8 *localBuffer = -				    yaffs_GetTempBuffer(dev, __LINE__); -				yaffs_ReadChunkDataFromObject(in, chunk, -							      localBuffer); -#ifdef CONFIG_YAFFS_WINCE -				yfsd_UnlockYAFFS(TRUE); -#endif -				memcpy(buffer, &localBuffer[start], nToCopy); +				u8 *local_buffer = +				    yaffs_get_temp_buffer(dev); +				yaffs_rd_data_obj(in, chunk, local_buffer); -#ifdef CONFIG_YAFFS_WINCE -				yfsd_LockYAFFS(TRUE); -#endif -				yaffs_ReleaseTempBuffer(dev, localBuffer, -							__LINE__); -			} +				memcpy(buffer, &local_buffer[start], n_copy); +				yaffs_release_temp_buffer(dev, local_buffer); +			}  		} else { -#ifdef CONFIG_YAFFS_WINCE -			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); - -			/* Under WinCE can't do direct transfer. Need to use a local buffer. -			 * This is because we otherwise screw up WinCE's memory mapper -			 */ -			yaffs_ReadChunkDataFromObject(in, chunk, localBuffer); - -#ifdef CONFIG_YAFFS_WINCE -			yfsd_UnlockYAFFS(TRUE); -#endif -			memcpy(buffer, localBuffer, dev->nDataBytesPerChunk); - -#ifdef CONFIG_YAFFS_WINCE -			yfsd_LockYAFFS(TRUE); -			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); -#endif - -#else -			/* A full chunk. Read directly into the supplied buffer. */ -			yaffs_ReadChunkDataFromObject(in, chunk, buffer); -#endif +			/* A full chunk. Read directly into the buffer. */ +			yaffs_rd_data_obj(in, chunk, buffer);  		} - -		n -= nToCopy; -		offset += nToCopy; -		buffer += nToCopy; -		nDone += nToCopy; - +		n -= n_copy; +		offset += n_copy; +		buffer += n_copy; +		n_done += n_copy;  	} - -	return nDone; +	return n_done;  } -int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset, -			  int nBytes, int writeThrough) +int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, +		     int n_bytes, int write_through)  { -	__u32 chunk = 0; -	__u32 start = 0; -	int nToCopy; -	int n = nBytes; -	int nDone = 0; -	int nToWriteBack; -	int startOfWrite = offset; -	int chunkWritten = 0; -	int nBytesRead; +	int chunk; +	u32 start; +	int n_copy; +	int n = n_bytes; +	int n_done = 0; +	int n_writeback; +	loff_t start_write = offset; +	int chunk_written = 0; +	u32 n_bytes_read; +	loff_t chunk_start; +	struct yaffs_dev *dev; -	yaffs_Device *dev; +	dev = in->my_dev; -	dev = in->myDev; +	while (n > 0 && chunk_written >= 0) { +		yaffs_addr_to_chunk(dev, offset, &chunk, &start); -	while (n > 0 && chunkWritten >= 0) { -		//chunk = offset / dev->nDataBytesPerChunk + 1; -		//start = offset % dev->nDataBytesPerChunk; -		yaffs_AddrToChunk(dev,offset,&chunk,&start); -		chunk++; +		if (((loff_t)chunk) * +		    dev->data_bytes_per_chunk + start != offset || +		    start >= dev->data_bytes_per_chunk) { +			yaffs_trace(YAFFS_TRACE_ERROR, +				"AddrToChunk of offset %lld gives chunk %d start %d", +				offset, chunk, start); +		} +		chunk++;	/* File pos to chunk in file offset */  		/* OK now check for the curveball where the start and end are in  		 * the same chunk.  		 */ -		if ((start + n) < dev->nDataBytesPerChunk) { -			nToCopy = n; +		if ((start + n) < dev->data_bytes_per_chunk) { +			n_copy = n; -			/* Now folks, to calculate how many bytes to write back.... -			 * If we're overwriting and not writing to then end of file then -			 * we need to write back as much as was there before. +			/* Now calculate how many bytes to write back.... +			 * If we're overwriting and not writing to then end of +			 * file then we need to write back as much as was there +			 * before.  			 */ -			nBytesRead = -			    in->variant.fileVariant.fileSize - -			    ((chunk - 1) * dev->nDataBytesPerChunk); +			chunk_start = (((loff_t)(chunk - 1)) * +					dev->data_bytes_per_chunk); -			if (nBytesRead > dev->nDataBytesPerChunk) { -				nBytesRead = dev->nDataBytesPerChunk; -			} +			if (chunk_start > in->variant.file_variant.file_size) +				n_bytes_read = 0;	/* Past end of file */ +			else +				n_bytes_read = +				    in->variant.file_variant.file_size - +				    chunk_start; + +			if (n_bytes_read > dev->data_bytes_per_chunk) +				n_bytes_read = dev->data_bytes_per_chunk; -			nToWriteBack = -			    (nBytesRead > -			     (start + n)) ? nBytesRead : (start + n); +			n_writeback = +			    (n_bytes_read > +			     (start + n)) ? n_bytes_read : (start + n); + +			if (n_writeback < 0 || +			    n_writeback > dev->data_bytes_per_chunk) +				BUG();  		} else { -			nToCopy = dev->nDataBytesPerChunk - start; -			nToWriteBack = dev->nDataBytesPerChunk; +			n_copy = dev->data_bytes_per_chunk - start; +			n_writeback = dev->data_bytes_per_chunk;  		} -		if (nToCopy != dev->nDataBytesPerChunk) { -			/* An incomplete start or end chunk (or maybe both start and end chunk) */ -			if (dev->nShortOpCaches > 0) { -				yaffs_ChunkCache *cache; -				/* If we can't find the data in the cache, then load the cache */ -				cache = yaffs_FindChunkCache(in, chunk); +		if (n_copy != dev->data_bytes_per_chunk || +		    dev->param.inband_tags) { +			/* An incomplete start or end chunk (or maybe both +			 * start and end chunk), or we're using inband tags, +			 * so we want to use the cache buffers. +			 */ +			if (dev->param.n_caches > 0) { +				struct yaffs_cache *cache; + +				/* If we can't find the data in the cache, then +				 * load the cache */ +				cache = yaffs_find_chunk_cache(in, chunk); -				if (!cache -				    && yaffs_CheckSpaceForAllocation(in-> -								     myDev)) { -					cache = yaffs_GrabChunkCache(in->myDev); +				if (!cache && +				    yaffs_check_alloc_available(dev, 1)) { +					cache = yaffs_grab_chunk_cache(dev);  					cache->object = in; -					cache->chunkId = chunk; +					cache->chunk_id = chunk;  					cache->dirty = 0;  					cache->locked = 0; -					yaffs_ReadChunkDataFromObject(in, chunk, -								      cache-> -								      data); -				} -				else if(cache && -					!cache->dirty && -					!yaffs_CheckSpaceForAllocation(in->myDev)){ -					/* Drop the cache if it was a read cache item and -					 * no space check has been made for it. +					yaffs_rd_data_obj(in, chunk, +							  cache->data); +				} else if (cache && +					   !cache->dirty && +					   !yaffs_check_alloc_available(dev, +									1)) { +					/* Drop the cache if it was a read cache +					 * item and no space check has been made +					 * for it.  					 */ -					 cache = NULL; +					cache = NULL;  				}  				if (cache) { -					yaffs_UseChunkCache(dev, cache, 1); +					yaffs_use_cache(dev, cache, 1);  					cache->locked = 1; -#ifdef CONFIG_YAFFS_WINCE -					yfsd_UnlockYAFFS(TRUE); -#endif  					memcpy(&cache->data[start], buffer, -					       nToCopy); +					       n_copy); -#ifdef CONFIG_YAFFS_WINCE -					yfsd_LockYAFFS(TRUE); -#endif  					cache->locked = 0; -					cache->nBytes = nToWriteBack; +					cache->n_bytes = n_writeback; -					if (writeThrough) { -						chunkWritten = -						    yaffs_WriteChunkDataToObject +					if (write_through) { +						chunk_written = +						    yaffs_wr_data_obj  						    (cache->object, -						     cache->chunkId, -						     cache->data, cache->nBytes, -						     1); +						     cache->chunk_id, +						     cache->data, +						     cache->n_bytes, 1);  						cache->dirty = 0;  					} -  				} else { -					chunkWritten = -1;	/* fail the write */ +					chunk_written = -1;	/* fail write */  				}  			} else { -				/* An incomplete start or end chunk (or maybe both start and end chunk) -				 * Read into the local buffer then copy, then copy over and write back. +				/* An incomplete start or end chunk (or maybe +				 * both start and end chunk). Read into the +				 * local buffer then copy over and write back.  				 */ -				__u8 *localBuffer = -				    yaffs_GetTempBuffer(dev, __LINE__); +				u8 *local_buffer = yaffs_get_temp_buffer(dev); -				yaffs_ReadChunkDataFromObject(in, chunk, -							      localBuffer); - -#ifdef CONFIG_YAFFS_WINCE -				yfsd_UnlockYAFFS(TRUE); -#endif +				yaffs_rd_data_obj(in, chunk, local_buffer); +				memcpy(&local_buffer[start], buffer, n_copy); -				memcpy(&localBuffer[start], buffer, nToCopy); - -#ifdef CONFIG_YAFFS_WINCE -				yfsd_LockYAFFS(TRUE); -#endif -				chunkWritten = -				    yaffs_WriteChunkDataToObject(in, chunk, -								 localBuffer, -								 nToWriteBack, -								 0); - -				yaffs_ReleaseTempBuffer(dev, localBuffer, -							__LINE__); +				chunk_written = +				    yaffs_wr_data_obj(in, chunk, +						      local_buffer, +						      n_writeback, 0); +				yaffs_release_temp_buffer(dev, local_buffer);  			} -  		} else { +			/* A full chunk. Write directly from the buffer. */ -#ifdef CONFIG_YAFFS_WINCE -			/* Under WinCE can't do direct transfer. Need to use a local buffer. -			 * This is because we otherwise screw up WinCE's memory mapper -			 */ -			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); -#ifdef CONFIG_YAFFS_WINCE -			yfsd_UnlockYAFFS(TRUE); -#endif -			memcpy(localBuffer, buffer, dev->nDataBytesPerChunk); -#ifdef CONFIG_YAFFS_WINCE -			yfsd_LockYAFFS(TRUE); -#endif -			chunkWritten = -			    yaffs_WriteChunkDataToObject(in, chunk, localBuffer, -							 dev->nDataBytesPerChunk, -							 0); -			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); -#else -			/* A full chunk. Write directly from the supplied buffer. */ -			chunkWritten = -			    yaffs_WriteChunkDataToObject(in, chunk, buffer, -							 dev->nDataBytesPerChunk, -							 0); -#endif -			/* Since we've overwritten the cached data, we better invalidate it. */ -			yaffs_InvalidateChunkCache(in, chunk); -		} +			chunk_written = +			    yaffs_wr_data_obj(in, chunk, buffer, +					      dev->data_bytes_per_chunk, 0); -		if (chunkWritten >= 0) { -			n -= nToCopy; -			offset += nToCopy; -			buffer += nToCopy; -			nDone += nToCopy; +			/* Since we've overwritten the cached data, +			 * we better invalidate it. */ +			yaffs_invalidate_chunk_cache(in, chunk);  		} +		if (chunk_written >= 0) { +			n -= n_copy; +			offset += n_copy; +			buffer += n_copy; +			n_done += n_copy; +		}  	}  	/* Update file object */ -	if ((startOfWrite + nDone) > in->variant.fileVariant.fileSize) { -		in->variant.fileVariant.fileSize = (startOfWrite + nDone); -	} +	if ((start_write + n_done) > in->variant.file_variant.file_size) +		in->variant.file_variant.file_size = (start_write + n_done);  	in->dirty = 1; - -	return nDone; +	return n_done;  } +int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, +		  int n_bytes, int write_through) +{ +	yaffs2_handle_hole(in, offset); +	return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); +}  /* ---------------------- File resizing stuff ------------------ */ -static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize) +static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size)  { -	yaffs_Device *dev = in->myDev; -	int oldFileSize = in->variant.fileVariant.fileSize; +	struct yaffs_dev *dev = in->my_dev; +	loff_t old_size = in->variant.file_variant.file_size; +	int i; +	int chunk_id; +	u32 dummy; +	int last_del; +	int start_del; -	int lastDel = 1 + (oldFileSize - 1) / dev->nDataBytesPerChunk; +	if (old_size > 0) +		yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); +	else +		last_del = 0; -	int startDel = 1 + (newSize + dev->nDataBytesPerChunk - 1) / -	    dev->nDataBytesPerChunk; -	int i; -	int chunkId; +	yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, +				&start_del, &dummy); +	last_del++; +	start_del++;  	/* Delete backwards so that we don't end up with holes if  	 * power is lost part-way through the operation.  	 */ -	for (i = lastDel; i >= startDel; i--) { +	for (i = last_del; i >= start_del; i--) {  		/* NB this could be optimised somewhat,  		 * eg. could retrieve the tags and write them without -		 * using yaffs_DeleteChunk +		 * using yaffs_chunk_del  		 */ -		chunkId = yaffs_FindAndDeleteChunkInFile(in, i, NULL); -		if (chunkId > 0) { -			if (chunkId < -			    (dev->internalStartBlock * dev->nChunksPerBlock) -			    || chunkId >= -			    ((dev->internalEndBlock + -			      1) * dev->nChunksPerBlock)) { -				T(YAFFS_TRACE_ALWAYS, -				  (TSTR("Found daft chunkId %d for %d" TENDSTR), -				   chunkId, i)); -			} else { -				in->nDataChunks--; -				yaffs_DeleteChunk(dev, chunkId, 1, __LINE__); -			} +		chunk_id = yaffs_find_del_file_chunk(in, i, NULL); + +		if (chunk_id < 1) +			continue; + +		if (chunk_id < +		    (dev->internal_start_block * dev->param.chunks_per_block) || +		    chunk_id >= +		    ((dev->internal_end_block + 1) * +		      dev->param.chunks_per_block)) { +			yaffs_trace(YAFFS_TRACE_ALWAYS, +				"Found daft chunk_id %d for %d", +				chunk_id, i); +		} else { +			in->n_data_chunks--; +			yaffs_chunk_del(dev, chunk_id, 1, __LINE__);  		}  	} -  } -int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize) +void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size)  { +	int new_full; +	u32 new_partial; +	struct yaffs_dev *dev = obj->my_dev; -	int oldFileSize = in->variant.fileVariant.fileSize; -	__u32 newSizeOfPartialChunk = 0; -	__u32 newFullChunks = 0; +	yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); -	yaffs_Device *dev = in->myDev; +	yaffs_prune_chunks(obj, new_size); -	yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk); +	if (new_partial != 0) { +		int last_chunk = 1 + new_full; +		u8 *local_buffer = yaffs_get_temp_buffer(dev); -	yaffs_FlushFilesChunkCache(in); -	yaffs_InvalidateWholeChunkCache(in); +		/* Rewrite the last chunk with its new size and zero pad */ +		yaffs_rd_data_obj(obj, last_chunk, local_buffer); +		memset(local_buffer + new_partial, 0, +		       dev->data_bytes_per_chunk - new_partial); -	yaffs_CheckGarbageCollection(dev); - -	if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { -		return yaffs_GetFileSize(in); -	} +		yaffs_wr_data_obj(obj, last_chunk, local_buffer, +				  new_partial, 1); -	if (newSize == oldFileSize) { -		return oldFileSize; +		yaffs_release_temp_buffer(dev, local_buffer);  	} -	if (newSize < oldFileSize) { +	obj->variant.file_variant.file_size = new_size; -		yaffs_PruneResizedChunks(in, newSize); - -		if (newSizeOfPartialChunk != 0) { -			int lastChunk = 1 + newFullChunks; - -			__u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); +	yaffs_prune_tree(dev, &obj->variant.file_variant); +} -			/* Got to read and rewrite the last chunk with its new size and zero pad */ -			yaffs_ReadChunkDataFromObject(in, lastChunk, -						      localBuffer); +int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) +{ +	struct yaffs_dev *dev = in->my_dev; +	loff_t old_size = in->variant.file_variant.file_size; -			memset(localBuffer + newSizeOfPartialChunk, 0, -			       dev->nDataBytesPerChunk - newSizeOfPartialChunk); +	yaffs_flush_file_cache(in); +	yaffs_invalidate_whole_cache(in); -			yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer, -						     newSizeOfPartialChunk, 1); +	yaffs_check_gc(dev, 0); -			yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); -		} +	if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) +		return YAFFS_FAIL; -		in->variant.fileVariant.fileSize = newSize; +	if (new_size == old_size) +		return YAFFS_OK; -		yaffs_PruneFileStructure(dev, &in->variant.fileVariant); +	if (new_size > old_size) { +		yaffs2_handle_hole(in, new_size); +		in->variant.file_variant.file_size = new_size;  	} else { -		/* newsSize > oldFileSize */ -		in->variant.fileVariant.fileSize = newSize; +		/* new_size < old_size */ +		yaffs_resize_file_down(in, new_size);  	} - - -	/* Write a new object header. +	/* Write a new object header to reflect the resize.  	 * show we've shrunk the file, if need be -	 * Do this only if the file is not in the deleted directories. +	 * Do this only if the file is not in the deleted directories +	 * and is not shadowed.  	 */ -	if (in->parent->objectId != YAFFS_OBJECTID_UNLINKED && -	    in->parent->objectId != YAFFS_OBJECTID_DELETED) { -		yaffs_UpdateObjectHeader(in, NULL, 0, -					 (newSize < oldFileSize) ? 1 : 0, 0); -	} +	if (in->parent && +	    !in->is_shadowed && +	    in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && +	    in->parent->obj_id != YAFFS_OBJECTID_DELETED) +		yaffs_update_oh(in, NULL, 0, 0, 0, NULL);  	return YAFFS_OK;  } -loff_t yaffs_GetFileSize(yaffs_Object * obj) +int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync)  { -	obj = yaffs_GetEquivalentObject(obj); - -	switch (obj->variantType) { -	case YAFFS_OBJECT_TYPE_FILE: -		return obj->variant.fileVariant.fileSize; -	case YAFFS_OBJECT_TYPE_SYMLINK: -		return yaffs_strlen(obj->variant.symLinkVariant.alias); -	default: -		return 0; -	} -} - - - -int yaffs_FlushFile(yaffs_Object * in, int updateTime) -{ -	int retVal; -	if (in->dirty) { -		yaffs_FlushFilesChunkCache(in); -		if (updateTime) { -#ifdef CONFIG_YAFFS_WINCE -			yfsd_WinFileTimeNow(in->win_mtime); -#else - -			in->yst_mtime = Y_CURRENT_TIME; +	if (!in->dirty) +		return YAFFS_OK; -#endif -		} +	yaffs_flush_file_cache(in); -		retVal = -		    (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >= -		     0) ? YAFFS_OK : YAFFS_FAIL; -	} else { -		retVal = YAFFS_OK; -	} +	if (data_sync) +		return YAFFS_OK; -	return retVal; +	if (update_time) +		yaffs_load_current_time(in, 0, 0); +	return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? +				YAFFS_OK : YAFFS_FAIL;  } -static int yaffs_DoGenericObjectDeletion(yaffs_Object * in) -{ -	/* First off, invalidate the file's data in the cache, without flushing. */ -	yaffs_InvalidateWholeChunkCache(in); - -	if (in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir)) { -		/* Move to the unlinked directory so we have a record that it was deleted. */ -		yaffs_ChangeObjectName(in, in->myDev->deletedDir,"deleted", 0, 0); - -	} - -	yaffs_RemoveObjectFromDirectory(in); -	yaffs_DeleteChunk(in->myDev, in->chunkId, 1, __LINE__); -	in->chunkId = -1; - -	yaffs_FreeObject(in); -	return YAFFS_OK; - -} - -/* yaffs_DeleteFile deletes the whole file data +/* yaffs_del_file deletes the whole file data   * and the inode associated with the file.   * It does not delete the links associated with the file.   */ -static int yaffs_UnlinkFile(yaffs_Object * in) +static int yaffs_unlink_file_if_needed(struct yaffs_obj *in)  { +	int ret_val; +	int del_now = 0; +	struct yaffs_dev *dev = in->my_dev; -	int retVal; -	int immediateDeletion = 0; - -	if (1) { -/* XXX U-BOOT XXX */ -#if 0 -#ifdef __KERNEL__ -		if (!in->myInode) { -			immediateDeletion = 1; - -		} -#endif -#else -		if (in->inUse <= 0) { -			immediateDeletion = 1; - -		} -#endif -		if (immediateDeletion) { -			retVal = -			    yaffs_ChangeObjectName(in, in->myDev->deletedDir, -						   "deleted", 0, 0); -			T(YAFFS_TRACE_TRACING, -			  (TSTR("yaffs: immediate deletion of file %d" TENDSTR), -			   in->objectId)); -			in->deleted = 1; -			in->myDev->nDeletedFiles++; -			if (0 && in->myDev->isYaffs2) { -				yaffs_ResizeFile(in, 0); -			} -			yaffs_SoftDeleteFile(in); -		} else { -			retVal = -			    yaffs_ChangeObjectName(in, in->myDev->unlinkedDir, -						   "unlinked", 0, 0); -		} +	if (!in->my_inode) +		del_now = 1; +	if (del_now) { +		ret_val = +		    yaffs_change_obj_name(in, in->my_dev->del_dir, +					  _Y("deleted"), 0, 0); +		yaffs_trace(YAFFS_TRACE_TRACING, +			"yaffs: immediate deletion of file %d", +			in->obj_id); +		in->deleted = 1; +		in->my_dev->n_deleted_files++; +		if (dev->param.disable_soft_del || dev->param.is_yaffs2) +			yaffs_resize_file(in, 0); +		yaffs_soft_del_file(in); +	} else { +		ret_val = +		    yaffs_change_obj_name(in, in->my_dev->unlinked_dir, +					  _Y("unlinked"), 0, 0);  	} -	return retVal; +	return ret_val;  } -int yaffs_DeleteFile(yaffs_Object * in) +int yaffs_del_file(struct yaffs_obj *in)  { -	int retVal = YAFFS_OK; +	int ret_val = YAFFS_OK; +	int deleted;	/* Need to cache value on stack if in is freed */ +	struct yaffs_dev *dev = in->my_dev; -	if (in->nDataChunks > 0) { -		/* Use soft deletion if there is data in the file */ -		if (!in->unlinked) { -			retVal = yaffs_UnlinkFile(in); -		} -		if (retVal == YAFFS_OK && in->unlinked && !in->deleted) { +	if (dev->param.disable_soft_del || dev->param.is_yaffs2) +		yaffs_resize_file(in, 0); + +	if (in->n_data_chunks > 0) { +		/* Use soft deletion if there is data in the file. +		 * That won't be the case if it has been resized to zero. +		 */ +		if (!in->unlinked) +			ret_val = yaffs_unlink_file_if_needed(in); + +		deleted = in->deleted; + +		if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) {  			in->deleted = 1; -			in->myDev->nDeletedFiles++; -			yaffs_SoftDeleteFile(in); +			deleted = 1; +			in->my_dev->n_deleted_files++; +			yaffs_soft_del_file(in);  		} -		return in->deleted ? YAFFS_OK : YAFFS_FAIL; +		return deleted ? YAFFS_OK : YAFFS_FAIL;  	} else {  		/* The file has no data chunks so we toss it immediately */ -		yaffs_FreeTnode(in->myDev, in->variant.fileVariant.top); -		in->variant.fileVariant.top = NULL; -		yaffs_DoGenericObjectDeletion(in); +		yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); +		in->variant.file_variant.top = NULL; +		yaffs_generic_obj_del(in);  		return YAFFS_OK;  	}  } -static int yaffs_DeleteDirectory(yaffs_Object * in) +int yaffs_is_non_empty_dir(struct yaffs_obj *obj)  { -	/* First check that the directory is empty. */ -	if (list_empty(&in->variant.directoryVariant.children)) { -		return yaffs_DoGenericObjectDeletion(in); -	} +	return (obj && +		obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && +		!(list_empty(&obj->variant.dir_variant.children)); +} -	return YAFFS_FAIL; +static int yaffs_del_dir(struct yaffs_obj *obj) +{ +	/* First check that the directory is empty. */ +	if (yaffs_is_non_empty_dir(obj)) +		return YAFFS_FAIL; +	return yaffs_generic_obj_del(obj);  } -static int yaffs_DeleteSymLink(yaffs_Object * in) +static int yaffs_del_symlink(struct yaffs_obj *in)  { -	YFREE(in->variant.symLinkVariant.alias); +	kfree(in->variant.symlink_variant.alias); +	in->variant.symlink_variant.alias = NULL; -	return yaffs_DoGenericObjectDeletion(in); +	return yaffs_generic_obj_del(in);  } -static int yaffs_DeleteHardLink(yaffs_Object * in) +static int yaffs_del_link(struct yaffs_obj *in)  { -	/* remove this hardlink from the list assocaited with the equivalent +	/* remove this hardlink from the list associated with the equivalent  	 * object  	 */ -	list_del(&in->hardLinks); -	return yaffs_DoGenericObjectDeletion(in); +	list_del_init(&in->hard_links); +	return yaffs_generic_obj_del(in);  } -static void yaffs_DestroyObject(yaffs_Object * obj) +int yaffs_del_obj(struct yaffs_obj *obj)  { -	switch (obj->variantType) { +	int ret_val = -1; + +	switch (obj->variant_type) {  	case YAFFS_OBJECT_TYPE_FILE: -		yaffs_DeleteFile(obj); +		ret_val = yaffs_del_file(obj);  		break;  	case YAFFS_OBJECT_TYPE_DIRECTORY: -		yaffs_DeleteDirectory(obj); +		if (!list_empty(&obj->variant.dir_variant.dirty)) { +			yaffs_trace(YAFFS_TRACE_BACKGROUND, +				"Remove object %d from dirty directories", +				obj->obj_id); +			list_del_init(&obj->variant.dir_variant.dirty); +		} +		return yaffs_del_dir(obj);  		break;  	case YAFFS_OBJECT_TYPE_SYMLINK: -		yaffs_DeleteSymLink(obj); +		ret_val = yaffs_del_symlink(obj);  		break;  	case YAFFS_OBJECT_TYPE_HARDLINK: -		yaffs_DeleteHardLink(obj); +		ret_val = yaffs_del_link(obj);  		break;  	case YAFFS_OBJECT_TYPE_SPECIAL: -		yaffs_DoGenericObjectDeletion(obj); +		ret_val = yaffs_generic_obj_del(obj);  		break;  	case YAFFS_OBJECT_TYPE_UNKNOWN: +		ret_val = 0;  		break;		/* should not happen. */  	} +	return ret_val;  } -static int yaffs_UnlinkWorker(yaffs_Object * obj) +static int yaffs_unlink_worker(struct yaffs_obj *obj)  { +	int del_now = 0; + +	if (!obj) +		return YAFFS_FAIL; + +	if (!obj->my_inode) +		del_now = 1; + +	yaffs_update_parent(obj->parent); -	if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { -		return yaffs_DeleteHardLink(obj); -	} else if (!list_empty(&obj->hardLinks)) { +	if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { +		return yaffs_del_link(obj); +	} else if (!list_empty(&obj->hard_links)) {  		/* Curve ball: We're unlinking an object that has a hardlink.  		 *  		 * This problem arises because we are not strictly following @@ -5072,1638 +3985,532 @@ static int yaffs_UnlinkWorker(yaffs_Object * obj)  		 * Instead, we do the following:  		 * - Select a hardlink.  		 * - Unhook it from the hard links -		 * - Unhook it from its parent directory (so that the rename can work) +		 * - Move it from its parent directory so that the rename works.  		 * - Rename the object to the hardlink's name.  		 * - Delete the hardlink  		 */ -		yaffs_Object *hl; -		int retVal; +		struct yaffs_obj *hl; +		struct yaffs_obj *parent; +		int ret_val;  		YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -		hl = list_entry(obj->hardLinks.next, yaffs_Object, hardLinks); +		hl = list_entry(obj->hard_links.next, struct yaffs_obj, +				hard_links); -		list_del_init(&hl->hardLinks); -		list_del_init(&hl->siblings); +		yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); +		parent = hl->parent; -		yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1); +		list_del_init(&hl->hard_links); -		retVal = yaffs_ChangeObjectName(obj, hl->parent, name, 0, 0); +		yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); -		if (retVal == YAFFS_OK) { -			retVal = yaffs_DoGenericObjectDeletion(hl); -		} -		return retVal; +		ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); -	} else { -		switch (obj->variantType) { +		if (ret_val == YAFFS_OK) +			ret_val = yaffs_generic_obj_del(hl); + +		return ret_val; + +	} else if (del_now) { +		switch (obj->variant_type) {  		case YAFFS_OBJECT_TYPE_FILE: -			return yaffs_UnlinkFile(obj); +			return yaffs_del_file(obj);  			break;  		case YAFFS_OBJECT_TYPE_DIRECTORY: -			return yaffs_DeleteDirectory(obj); +			list_del_init(&obj->variant.dir_variant.dirty); +			return yaffs_del_dir(obj);  			break;  		case YAFFS_OBJECT_TYPE_SYMLINK: -			return yaffs_DeleteSymLink(obj); +			return yaffs_del_symlink(obj);  			break;  		case YAFFS_OBJECT_TYPE_SPECIAL: -			return yaffs_DoGenericObjectDeletion(obj); +			return yaffs_generic_obj_del(obj);  			break;  		case YAFFS_OBJECT_TYPE_HARDLINK:  		case YAFFS_OBJECT_TYPE_UNKNOWN:  		default:  			return YAFFS_FAIL;  		} +	} else if (yaffs_is_non_empty_dir(obj)) { +		return YAFFS_FAIL; +	} else { +		return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, +						_Y("unlinked"), 0, 0);  	}  } - -static int yaffs_UnlinkObject( yaffs_Object *obj) +static int yaffs_unlink_obj(struct yaffs_obj *obj)  { - -	if (obj && obj->unlinkAllowed) { -		return yaffs_UnlinkWorker(obj); -	} +	if (obj && obj->unlink_allowed) +		return yaffs_unlink_worker(obj);  	return YAFFS_FAIL; +} + +int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) +{ +	struct yaffs_obj *obj; +	obj = yaffs_find_by_name(dir, name); +	return yaffs_unlink_obj(obj);  } -int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name) + +/* Note: + * If old_name is NULL then we take old_dir as the object to be renamed. + */ +int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, +		     struct yaffs_obj *new_dir, const YCHAR *new_name)  { -	yaffs_Object *obj; +	struct yaffs_obj *obj = NULL; +	struct yaffs_obj *existing_target = NULL; +	int force = 0; +	int result; +	struct yaffs_dev *dev; + +	if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { +		BUG(); +		return YAFFS_FAIL; +	} +	if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { +		BUG(); +		return YAFFS_FAIL; +	} + +	dev = old_dir->my_dev; + +#ifdef CONFIG_YAFFS_CASE_INSENSITIVE +	/* Special case for case insemsitive systems. +	 * While look-up is case insensitive, the name isn't. +	 * Therefore we might want to change x.txt to X.txt +	 */ +	if (old_dir == new_dir && +		old_name && new_name && +		yaffs_strcmp(old_name, new_name) == 0) +		force = 1; +#endif + +	if (yaffs_strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > +	    YAFFS_MAX_NAME_LENGTH) +		/* ENAMETOOLONG */ +		return YAFFS_FAIL; -	obj = yaffs_FindObjectByName(dir, name); -	return yaffs_UnlinkObject(obj); +	if (old_name) +		obj = yaffs_find_by_name(old_dir, old_name); +	else{ +		obj = old_dir; +		old_dir = obj->parent; +	} + +	if (obj && obj->rename_allowed) { +		/* Now handle an existing target, if there is one */ +		existing_target = yaffs_find_by_name(new_dir, new_name); +		if (yaffs_is_non_empty_dir(existing_target)) { +			return YAFFS_FAIL;	/* ENOTEMPTY */ +		} else if (existing_target && existing_target != obj) { +			/* Nuke the target first, using shadowing, +			 * but only if it isn't the same object. +			 * +			 * Note we must disable gc here otherwise it can mess +			 * up the shadowing. +			 * +			 */ +			dev->gc_disable = 1; +			yaffs_change_obj_name(obj, new_dir, new_name, force, +					      existing_target->obj_id); +			existing_target->is_shadowed = 1; +			yaffs_unlink_obj(existing_target); +			dev->gc_disable = 0; +		} + +		result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); + +		yaffs_update_parent(old_dir); +		if (new_dir != old_dir) +			yaffs_update_parent(new_dir); + +		return result; +	} +	return YAFFS_FAIL;  }  /*----------------------- Initialisation Scanning ---------------------- */ -static void yaffs_HandleShadowedObject(yaffs_Device * dev, int objId, -				       int backwardScanning) +void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, +			       int backward_scanning)  { -	yaffs_Object *obj; - -	if (!backwardScanning) { -		/* Handle YAFFS1 forward scanning case -		 * For YAFFS1 we always do the deletion -		 */ +	struct yaffs_obj *obj; -	} else { +	if (backward_scanning) {  		/* Handle YAFFS2 case (backward scanning)  		 * If the shadowed object exists then ignore.  		 */ -		if (yaffs_FindObjectByNumber(dev, objId)) { +		obj = yaffs_find_by_number(dev, obj_id); +		if (obj)  			return; -		}  	} -	/* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc. +	/* Let's create it (if it does not exist) assuming it is a file so that +	 * it can do shrinking etc.  	 * We put it in unlinked dir to be cleaned up after the scanning  	 */  	obj = -	    yaffs_FindOrCreateObjectByNumber(dev, objId, -					     YAFFS_OBJECT_TYPE_FILE); -	yaffs_AddObjectToDirectory(dev->unlinkedDir, obj); -	obj->variant.fileVariant.shrinkSize = 0; -	obj->valid = 1;		/* So that we don't read any other info for this file */ - +	    yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); +	if (!obj) +		return; +	obj->is_shadowed = 1; +	yaffs_add_obj_to_dir(dev->unlinked_dir, obj); +	obj->variant.file_variant.shrink_size = 0; +	obj->valid = 1;		/* So that we don't read any other info. */  } -typedef struct { -	int seq; -	int block; -} yaffs_BlockIndex; - - -static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList) +void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list)  { -	yaffs_Object *hl; -	yaffs_Object *in; - -	while (hardList) { -		hl = hardList; -		hardList = (yaffs_Object *) (hardList->hardLinks.next); +	struct list_head *lh; +	struct list_head *save; +	struct yaffs_obj *hl; +	struct yaffs_obj *in; -		in = yaffs_FindObjectByNumber(dev, -					      hl->variant.hardLinkVariant. -					      equivalentObjectId); +	list_for_each_safe(lh, save, hard_list) { +		hl = list_entry(lh, struct yaffs_obj, hard_links); +		in = yaffs_find_by_number(dev, +					hl->variant.hardlink_variant.equiv_id);  		if (in) {  			/* Add the hardlink pointers */ -			hl->variant.hardLinkVariant.equivalentObject = in; -			list_add(&hl->hardLinks, &in->hardLinks); +			hl->variant.hardlink_variant.equiv_obj = in; +			list_add(&hl->hard_links, &in->hard_links);  		} else {  			/* Todo Need to report/handle this better.  			 * Got a problem... hardlink to a non-existant object  			 */ -			hl->variant.hardLinkVariant.equivalentObject = NULL; -			INIT_LIST_HEAD(&hl->hardLinks); - +			hl->variant.hardlink_variant.equiv_obj = NULL; +			INIT_LIST_HEAD(&hl->hard_links);  		} -  	} - -} - - - - - -static int ybicmp(const void *a, const void *b){ -    register int aseq = ((yaffs_BlockIndex *)a)->seq; -    register int bseq = ((yaffs_BlockIndex *)b)->seq; -    register int ablock = ((yaffs_BlockIndex *)a)->block; -    register int bblock = ((yaffs_BlockIndex *)b)->block; -    if( aseq == bseq ) -	return ablock - bblock; -    else -	return aseq - bseq; -  } -static int yaffs_Scan(yaffs_Device * dev) +static void yaffs_strip_deleted_objs(struct yaffs_dev *dev)  { -	yaffs_ExtendedTags tags; -	int blk; -	int blockIterator; -	int startIterator; -	int endIterator; -	int nBlocksToScan = 0; - -	int chunk; -	int c; -	int deleted; -	yaffs_BlockState state; -	yaffs_Object *hardList = NULL; -	yaffs_BlockInfo *bi; -	int sequenceNumber; -	yaffs_ObjectHeader *oh; -	yaffs_Object *in; -	yaffs_Object *parent; -	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; - -	int alloc_failed = 0; - - -	__u8 *chunkData; - -	yaffs_BlockIndex *blockIndex = NULL; - -	if (dev->isYaffs2) { -		T(YAFFS_TRACE_SCAN, -		  (TSTR("yaffs_Scan is not for YAFFS2!" TENDSTR))); -		return YAFFS_FAIL; -	} - -	//TODO  Throw all the yaffs2 stuuf out of yaffs_Scan since it is only for yaffs1 format. - -	T(YAFFS_TRACE_SCAN, -	  (TSTR("yaffs_Scan starts  intstartblk %d intendblk %d..." TENDSTR), -	   dev->internalStartBlock, dev->internalEndBlock)); - -	chunkData = yaffs_GetTempBuffer(dev, __LINE__); - -	dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; - -	if (dev->isYaffs2) { -		blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); -		if(!blockIndex) -			return YAFFS_FAIL; -	} - -	/* Scan all the blocks to determine their state */ -	for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { -		bi = yaffs_GetBlockInfo(dev, blk); -		yaffs_ClearChunkBits(dev, blk); -		bi->pagesInUse = 0; -		bi->softDeletions = 0; - -		yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); - -		bi->blockState = state; -		bi->sequenceNumber = sequenceNumber; - -		T(YAFFS_TRACE_SCAN_DEBUG, -		  (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, -		   state, sequenceNumber)); - -		if (state == YAFFS_BLOCK_STATE_DEAD) { -			T(YAFFS_TRACE_BAD_BLOCKS, -			  (TSTR("block %d is bad" TENDSTR), blk)); -		} else if (state == YAFFS_BLOCK_STATE_EMPTY) { -			T(YAFFS_TRACE_SCAN_DEBUG, -			  (TSTR("Block empty " TENDSTR))); -			dev->nErasedBlocks++; -			dev->nFreeChunks += dev->nChunksPerBlock; -		} else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { - -			/* Determine the highest sequence number */ -			if (dev->isYaffs2 && -			    sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && -			    sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { - -				blockIndex[nBlocksToScan].seq = sequenceNumber; -				blockIndex[nBlocksToScan].block = blk; - -				nBlocksToScan++; - -				if (sequenceNumber >= dev->sequenceNumber) { -					dev->sequenceNumber = sequenceNumber; -				} -			} else if (dev->isYaffs2) { -				/* TODO: Nasty sequence number! */ -				T(YAFFS_TRACE_SCAN, -				  (TSTR -				   ("Block scanning block %d has bad sequence number %d" -				    TENDSTR), blk, sequenceNumber)); - -			} -		} -	} - -	/* Sort the blocks -	 * Dungy old bubble sort for now... -	 */ -	if (dev->isYaffs2) { -		yaffs_BlockIndex temp; -		int i; -		int j; - -		for (i = 0; i < nBlocksToScan; i++) -			for (j = i + 1; j < nBlocksToScan; j++) -				if (blockIndex[i].seq > blockIndex[j].seq) { -					temp = blockIndex[j]; -					blockIndex[j] = blockIndex[i]; -					blockIndex[i] = temp; -				} -	} - -	/* Now scan the blocks looking at the data. */ -	if (dev->isYaffs2) { -		startIterator = 0; -		endIterator = nBlocksToScan - 1; -		T(YAFFS_TRACE_SCAN_DEBUG, -		  (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); -	} else { -		startIterator = dev->internalStartBlock; -		endIterator = dev->internalEndBlock; -	} - -	/* For each block.... */ -	for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; -	     blockIterator++) { - -		if (dev->isYaffs2) { -			/* get the block to scan in the correct order */ -			blk = blockIndex[blockIterator].block; -		} else { -			blk = blockIterator; -		} - -		bi = yaffs_GetBlockInfo(dev, blk); -		state = bi->blockState; - -		deleted = 0; - -		/* For each chunk in each block that needs scanning....*/ -		for (c = 0; !alloc_failed && c < dev->nChunksPerBlock && -		     state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { -			/* Read the tags and decide what to do */ -			chunk = blk * dev->nChunksPerBlock + c; - -			yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, -							&tags); - -			/* Let's have a good look at this chunk... */ - -			if (!dev->isYaffs2 && tags.chunkDeleted) { -				/* YAFFS1 only... -				 * A deleted chunk -				 */ -				deleted++; -				dev->nFreeChunks++; -				/*T((" %d %d deleted\n",blk,c)); */ -			} else if (!tags.chunkUsed) { -				/* An unassigned chunk in the block -				 * This means that either the block is empty or -				 * this is the one being allocated from -				 */ - -				if (c == 0) { -					/* We're looking at the first chunk in the block so the block is unused */ -					state = YAFFS_BLOCK_STATE_EMPTY; -					dev->nErasedBlocks++; -				} else { -					/* this is the block being allocated from */ -					T(YAFFS_TRACE_SCAN, -					  (TSTR -					   (" Allocating from %d %d" TENDSTR), -					   blk, c)); -					state = YAFFS_BLOCK_STATE_ALLOCATING; -					dev->allocationBlock = blk; -					dev->allocationPage = c; -					dev->allocationBlockFinder = blk; -					/* Set it to here to encourage the allocator to go forth from here. */ - -					/* Yaffs2 sanity check: -					 * This should be the one with the highest sequence number -					 */ -					if (dev->isYaffs2 -					    && (dev->sequenceNumber != -						bi->sequenceNumber)) { -						T(YAFFS_TRACE_ALWAYS, -						  (TSTR -						   ("yaffs: Allocation block %d was not highest sequence id:" -						    " block seq = %d, dev seq = %d" -						    TENDSTR), blk,bi->sequenceNumber,dev->sequenceNumber)); -					} -				} - -				dev->nFreeChunks += (dev->nChunksPerBlock - c); -			} else if (tags.chunkId > 0) { -				/* chunkId > 0 so it is a data chunk... */ -				unsigned int endpos; - -				yaffs_SetChunkBit(dev, blk, c); -				bi->pagesInUse++; - -				in = yaffs_FindOrCreateObjectByNumber(dev, -								      tags. -								      objectId, -								      YAFFS_OBJECT_TYPE_FILE); -				/* PutChunkIntoFile checks for a clash (two data chunks with -				 * the same chunkId). -				 */ - -				if(!in) -					alloc_failed = 1; - -				if(in){ -					if(!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,1)) -						alloc_failed = 1; -				} - -				endpos = -				    (tags.chunkId - 1) * dev->nDataBytesPerChunk + -				    tags.byteCount; -				if (in && -				    in->variantType == YAFFS_OBJECT_TYPE_FILE -				    && in->variant.fileVariant.scannedFileSize < -				    endpos) { -					in->variant.fileVariant. -					    scannedFileSize = endpos; -					if (!dev->useHeaderFileSize) { -						in->variant.fileVariant. -						    fileSize = -						    in->variant.fileVariant. -						    scannedFileSize; -					} - -				} -				/* T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));   */ -			} else { -				/* chunkId == 0, so it is an ObjectHeader. -				 * Thus, we read in the object header and make the object -				 */ -				yaffs_SetChunkBit(dev, blk, c); -				bi->pagesInUse++; - -				yaffs_ReadChunkWithTagsFromNAND(dev, chunk, -								chunkData, -								NULL); - -				oh = (yaffs_ObjectHeader *) chunkData; - -				in = yaffs_FindObjectByNumber(dev, -							      tags.objectId); -				if (in && in->variantType != oh->type) { -					/* This should not happen, but somehow -					 * Wev'e ended up with an objectId that has been reused but not yet -					 * deleted, and worse still it has changed type. Delete the old object. -					 */ - -					yaffs_DestroyObject(in); - -					in = 0; -				} - -				in = yaffs_FindOrCreateObjectByNumber(dev, -								      tags. -								      objectId, -								      oh->type); - -				if(!in) -					alloc_failed = 1; - -				if (in && oh->shadowsObject > 0) { -					yaffs_HandleShadowedObject(dev, -								   oh-> -								   shadowsObject, -								   0); -				} - -				if (in && in->valid) { -					/* We have already filled this one. We have a duplicate and need to resolve it. */ - -					unsigned existingSerial = in->serial; -					unsigned newSerial = tags.serialNumber; - -					if (dev->isYaffs2 || -					    ((existingSerial + 1) & 3) == -					    newSerial) { -						/* Use new one - destroy the exisiting one */ -						yaffs_DeleteChunk(dev, -								  in->chunkId, -								  1, __LINE__); -						in->valid = 0; -					} else { -						/* Use existing - destroy this one. */ -						yaffs_DeleteChunk(dev, chunk, 1, -								  __LINE__); -					} -				} - -				if (in && !in->valid && -				    (tags.objectId == YAFFS_OBJECTID_ROOT || -				     tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) { -					/* We only load some info, don't fiddle with directory structure */ -					in->valid = 1; -					in->variantType = oh->type; - -					in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE -					in->win_atime[0] = oh->win_atime[0]; -					in->win_ctime[0] = oh->win_ctime[0]; -					in->win_mtime[0] = oh->win_mtime[0]; -					in->win_atime[1] = oh->win_atime[1]; -					in->win_ctime[1] = oh->win_ctime[1]; -					in->win_mtime[1] = oh->win_mtime[1]; -#else -					in->yst_uid = oh->yst_uid; -					in->yst_gid = oh->yst_gid; -					in->yst_atime = oh->yst_atime; -					in->yst_mtime = oh->yst_mtime; -					in->yst_ctime = oh->yst_ctime; -					in->yst_rdev = oh->yst_rdev; -#endif -					in->chunkId = chunk; - -				} else if (in && !in->valid) { -					/* we need to load this info */ - -					in->valid = 1; -					in->variantType = oh->type; - -					in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE -					in->win_atime[0] = oh->win_atime[0]; -					in->win_ctime[0] = oh->win_ctime[0]; -					in->win_mtime[0] = oh->win_mtime[0]; -					in->win_atime[1] = oh->win_atime[1]; -					in->win_ctime[1] = oh->win_ctime[1]; -					in->win_mtime[1] = oh->win_mtime[1]; -#else -					in->yst_uid = oh->yst_uid; -					in->yst_gid = oh->yst_gid; -					in->yst_atime = oh->yst_atime; -					in->yst_mtime = oh->yst_mtime; -					in->yst_ctime = oh->yst_ctime; -					in->yst_rdev = oh->yst_rdev; -#endif -					in->chunkId = chunk; - -					yaffs_SetObjectName(in, oh->name); -					in->dirty = 0; - -					/* directory stuff... -					 * hook up to parent -					 */ - -					parent = -					    yaffs_FindOrCreateObjectByNumber -					    (dev, oh->parentObjectId, -					     YAFFS_OBJECT_TYPE_DIRECTORY); -					if (parent->variantType == -					    YAFFS_OBJECT_TYPE_UNKNOWN) { -						/* Set up as a directory */ -						parent->variantType = -						    YAFFS_OBJECT_TYPE_DIRECTORY; -						INIT_LIST_HEAD(&parent->variant. -							       directoryVariant. -							       children); -					} else if (parent->variantType != -						   YAFFS_OBJECT_TYPE_DIRECTORY) -					{ -						/* Hoosterman, another problem.... -						 * We're trying to use a non-directory as a directory -						 */ - -						T(YAFFS_TRACE_ERROR, -						  (TSTR -						   ("yaffs tragedy: attempting to use non-directory as" -						    " a directory in scan. Put in lost+found." -						    TENDSTR))); -						parent = dev->lostNFoundDir; -					} - -					yaffs_AddObjectToDirectory(parent, in); - -					if (0 && (parent == dev->deletedDir || -						  parent == dev->unlinkedDir)) { -						in->deleted = 1;	/* If it is unlinked at start up then it wants deleting */ -						dev->nDeletedFiles++; -					} -					/* 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->variantType) { -					case YAFFS_OBJECT_TYPE_UNKNOWN: -						/* Todo got a problem */ -						break; -					case YAFFS_OBJECT_TYPE_FILE: -						if (dev->isYaffs2 -						    && oh->isShrink) { -							/* Prune back the shrunken chunks */ -							yaffs_PruneResizedChunks -							    (in, oh->fileSize); -							/* Mark the block as having a shrinkHeader */ -							bi->hasShrinkHeader = 1; -						} - -						if (dev->useHeaderFileSize) - -							in->variant.fileVariant. -							    fileSize = -							    oh->fileSize; - -						break; -					case YAFFS_OBJECT_TYPE_HARDLINK: -						in->variant.hardLinkVariant. -						    equivalentObjectId = -						    oh->equivalentObjectId; -						in->hardLinks.next = -						    (struct list_head *) -						    hardList; -						hardList = in; -						break; -					case YAFFS_OBJECT_TYPE_DIRECTORY: -						/* Do nothing */ -						break; -					case YAFFS_OBJECT_TYPE_SPECIAL: -						/* Do nothing */ -						break; -					case YAFFS_OBJECT_TYPE_SYMLINK: -						in->variant.symLinkVariant.alias = -						    yaffs_CloneString(oh->alias); -						if(!in->variant.symLinkVariant.alias) -							alloc_failed = 1; -						break; -					} - -					if (parent == dev->deletedDir) { -						yaffs_DestroyObject(in); -						bi->hasShrinkHeader = 1; -					} -				} -			} -		} - -		if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { -			/* If we got this far while scanning, then the block is fully allocated.*/ -			state = YAFFS_BLOCK_STATE_FULL; -		} - -		bi->blockState = state; - -		/* Now let's see if it was dirty */ -		if (bi->pagesInUse == 0 && -		    !bi->hasShrinkHeader && -		    bi->blockState == YAFFS_BLOCK_STATE_FULL) { -			yaffs_BlockBecameDirty(dev, blk); -		} - -	} - -	if (blockIndex) { -		YFREE(blockIndex); -	} - - -	/* Ok, we've done all the scanning. -	 * Fix up the hard link chains. -	 * We should now have scanned all the objects, now it's time to add these -	 * hardlinks. +	/* +	 *  Sort out state of unlinked and deleted objects after scanning.  	 */ +	struct list_head *i; +	struct list_head *n; +	struct yaffs_obj *l; -	yaffs_HardlinkFixup(dev,hardList); - -	/* Handle the unlinked files. Since they were left in an unlinked state we should -	 * just delete them. -	 */ -	{ -		struct list_head *i; -		struct list_head *n; +	if (dev->read_only) +		return; -		yaffs_Object *l; -		/* Soft delete all the unlinked files */ -		list_for_each_safe(i, n, -				   &dev->unlinkedDir->variant.directoryVariant. -				   children) { -			if (i) { -				l = list_entry(i, yaffs_Object, siblings); -				yaffs_DestroyObject(l); -			} -		} +	/* Soft delete all the unlinked files */ +	list_for_each_safe(i, n, +			   &dev->unlinked_dir->variant.dir_variant.children) { +		l = list_entry(i, struct yaffs_obj, siblings); +		yaffs_del_obj(l);  	} -	yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); - -	if(alloc_failed){ -		return YAFFS_FAIL; +	list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { +		l = list_entry(i, struct yaffs_obj, siblings); +		yaffs_del_obj(l);  	} - -	T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR))); - - -	return YAFFS_OK;  } -static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) -{ -	__u8 *chunkData; -	yaffs_ObjectHeader *oh; -	yaffs_Device *dev = in->myDev; -	yaffs_ExtendedTags tags; - -	if(!in) -		return; - -#if 0 -	T(YAFFS_TRACE_SCAN,(TSTR("details for object %d %s loaded" TENDSTR), -		in->objectId, -		in->lazyLoaded ? "not yet" : "already")); -#endif - -	if(in->lazyLoaded){ -		in->lazyLoaded = 0; -		chunkData = yaffs_GetTempBuffer(dev, __LINE__); - -		yaffs_ReadChunkWithTagsFromNAND(dev, in->chunkId, -						chunkData, &tags); -		oh = (yaffs_ObjectHeader *) chunkData; - -		in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE -		in->win_atime[0] = oh->win_atime[0]; -		in->win_ctime[0] = oh->win_ctime[0]; -		in->win_mtime[0] = oh->win_mtime[0]; -		in->win_atime[1] = oh->win_atime[1]; -		in->win_ctime[1] = oh->win_ctime[1]; -		in->win_mtime[1] = oh->win_mtime[1]; -#else -		in->yst_uid = oh->yst_uid; -		in->yst_gid = oh->yst_gid; -		in->yst_atime = oh->yst_atime; -		in->yst_mtime = oh->yst_mtime; -		in->yst_ctime = oh->yst_ctime; -		in->yst_rdev = oh->yst_rdev; - -#endif -		yaffs_SetObjectName(in, oh->name); - -		if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK){ -			 in->variant.symLinkVariant.alias = -						    yaffs_CloneString(oh->alias); -		} +/* + * This code iterates through all the objects making sure that they are rooted. + * Any unrooted objects are re-rooted in lost+found. + * An object needs to be in one of: + * - Directly under deleted, unlinked + * - Directly or indirectly under root. + * + * Note: + *  This code assumes that we don't ever change the current relationships + *  between directories: + *   root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL + *   lost-n-found->parent == root_dir + * + * This fixes the problem where directories might have inadvertently been + * deleted leaving the object "hanging" without being rooted in the + * directory tree. + */ -		yaffs_ReleaseTempBuffer(dev,chunkData, __LINE__); -	} +static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) +{ +	return (obj == dev->del_dir || +		obj == dev->unlinked_dir || obj == dev->root_dir);  } -static int yaffs_ScanBackwards(yaffs_Device * dev) +static void yaffs_fix_hanging_objs(struct yaffs_dev *dev)  { -	yaffs_ExtendedTags tags; -	int blk; -	int blockIterator; -	int startIterator; -	int endIterator; -	int nBlocksToScan = 0; - -	int chunk; -	int c; -	yaffs_BlockState state; -	yaffs_Object *hardList = NULL; -	yaffs_BlockInfo *bi; -	int sequenceNumber; -	yaffs_ObjectHeader *oh; -	yaffs_Object *in; -	yaffs_Object *parent; -	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; -	int itsUnlinked; -	__u8 *chunkData; - -	int fileSize; -	int isShrink; -	int foundChunksInBlock; -	int equivalentObjectId; -	int alloc_failed = 0; - - -	yaffs_BlockIndex *blockIndex = NULL; -	int altBlockIndex = 0; - -	if (!dev->isYaffs2) { -		T(YAFFS_TRACE_SCAN, -		  (TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR))); -		return YAFFS_FAIL; -	} - -	T(YAFFS_TRACE_SCAN, -	  (TSTR -	   ("yaffs_ScanBackwards starts  intstartblk %d intendblk %d..." -	    TENDSTR), dev->internalStartBlock, dev->internalEndBlock)); - - -	dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; - -	blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); - -	if(!blockIndex) { -		blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex)); -		altBlockIndex = 1; -	} - -	if(!blockIndex) { -		T(YAFFS_TRACE_SCAN, -		  (TSTR("yaffs_Scan() could not allocate block index!" TENDSTR))); -		return YAFFS_FAIL; -	} - -	dev->blocksInCheckpoint = 0; - -	chunkData = yaffs_GetTempBuffer(dev, __LINE__); - -	/* Scan all the blocks to determine their state */ -	for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { -		bi = yaffs_GetBlockInfo(dev, blk); -		yaffs_ClearChunkBits(dev, blk); -		bi->pagesInUse = 0; -		bi->softDeletions = 0; - -		yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); - -		bi->blockState = state; -		bi->sequenceNumber = sequenceNumber; - -		if(bi->sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) -			bi->blockState = state = YAFFS_BLOCK_STATE_CHECKPOINT; - -		T(YAFFS_TRACE_SCAN_DEBUG, -		  (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, -		   state, sequenceNumber)); - - -		if(state == YAFFS_BLOCK_STATE_CHECKPOINT){ -			dev->blocksInCheckpoint++; - -		} else if (state == YAFFS_BLOCK_STATE_DEAD) { -			T(YAFFS_TRACE_BAD_BLOCKS, -			  (TSTR("block %d is bad" TENDSTR), blk)); -		} else if (state == YAFFS_BLOCK_STATE_EMPTY) { -			T(YAFFS_TRACE_SCAN_DEBUG, -			  (TSTR("Block empty " TENDSTR))); -			dev->nErasedBlocks++; -			dev->nFreeChunks += dev->nChunksPerBlock; -		} else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { - -			/* Determine the highest sequence number */ -			if (dev->isYaffs2 && -			    sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && -			    sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { - -				blockIndex[nBlocksToScan].seq = sequenceNumber; -				blockIndex[nBlocksToScan].block = blk; - -				nBlocksToScan++; - -				if (sequenceNumber >= dev->sequenceNumber) { -					dev->sequenceNumber = sequenceNumber; -				} -			} else if (dev->isYaffs2) { -				/* TODO: Nasty sequence number! */ -				T(YAFFS_TRACE_SCAN, -				  (TSTR -				   ("Block scanning block %d has bad sequence number %d" -				    TENDSTR), blk, sequenceNumber)); - -			} -		} -	} - -	T(YAFFS_TRACE_SCAN, -	(TSTR("%d blocks to be sorted..." TENDSTR), nBlocksToScan)); - - - -	YYIELD(); - -	/* Sort the blocks */ -#ifndef CONFIG_YAFFS_USE_OWN_SORT -	{ -		/* Use qsort now. */ -		yaffs_qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp); -	} -#else -	{ -		/* Dungy old bubble sort... */ - -		yaffs_BlockIndex temp; -		int i; -		int j; - -		for (i = 0; i < nBlocksToScan; i++) -			for (j = i + 1; j < nBlocksToScan; j++) -				if (blockIndex[i].seq > blockIndex[j].seq) { -					temp = blockIndex[j]; -					blockIndex[j] = blockIndex[i]; -					blockIndex[i] = temp; -				} -	} -#endif - -	YYIELD(); - -	T(YAFFS_TRACE_SCAN, (TSTR("...done" TENDSTR))); - -	/* Now scan the blocks looking at the data. */ -	startIterator = 0; -	endIterator = nBlocksToScan - 1; -	T(YAFFS_TRACE_SCAN_DEBUG, -	  (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); - -	/* For each block.... backwards */ -	for (blockIterator = endIterator; !alloc_failed && blockIterator >= startIterator; -	     blockIterator--) { -		/* Cooperative multitasking! This loop can run for so -		   long that watchdog timers expire. */ -		YYIELD(); - -		/* get the block to scan in the correct order */ -		blk = blockIndex[blockIterator].block; - -		bi = yaffs_GetBlockInfo(dev, blk); - - -		state = bi->blockState; - -		/* For each chunk in each block that needs scanning.... */ -		foundChunksInBlock = 0; -		for (c = dev->nChunksPerBlock - 1; -		     !alloc_failed && c >= 0 && -		     (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || -		      state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { -			/* Scan backwards... -			 * Read the tags and decide what to do -			 */ - -			chunk = blk * dev->nChunksPerBlock + c; - -			yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, -							&tags); - -			/* Let's have a good look at this chunk... */ - -			if (!tags.chunkUsed) { -				/* 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(foundChunksInBlock) -				{ -					/* This is a chunk that was skipped due to failing the erased check */ - -				} else if (c == 0) { -					/* We're looking at the first chunk in the block so the block is unused */ -					state = YAFFS_BLOCK_STATE_EMPTY; -					dev->nErasedBlocks++; -				} else { -					if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || -					    state == YAFFS_BLOCK_STATE_ALLOCATING) { -						if(dev->sequenceNumber == bi->sequenceNumber) { -							/* this is the block being allocated from */ - -							T(YAFFS_TRACE_SCAN, -							  (TSTR -							   (" Allocating from %d %d" -							    TENDSTR), blk, c)); - -							state = YAFFS_BLOCK_STATE_ALLOCATING; -							dev->allocationBlock = blk; -							dev->allocationPage = c; -							dev->allocationBlockFinder = blk; -						} -						else { -							/* This is a partially written block that is not -							 * the current allocation block. This block must have -							 * had a write failure, so set up for retirement. -							 */ - -							 bi->needsRetiring = 1; -							 bi->gcPrioritise = 1; - -							 T(YAFFS_TRACE_ALWAYS, -							 (TSTR("Partially written block %d being set for retirement" TENDSTR), -							 blk)); -						} - -					} - -				} - -				dev->nFreeChunks++; - -			} else if (tags.chunkId > 0) { -				/* chunkId > 0 so it is a data chunk... */ -				unsigned int endpos; -				__u32 chunkBase = -				    (tags.chunkId - 1) * dev->nDataBytesPerChunk; - -				foundChunksInBlock = 1; - - -				yaffs_SetChunkBit(dev, blk, c); -				bi->pagesInUse++; - -				in = yaffs_FindOrCreateObjectByNumber(dev, -								      tags. -								      objectId, -								      YAFFS_OBJECT_TYPE_FILE); -				if(!in){ -					/* Out of memory */ -					alloc_failed = 1; -				} - -				if (in && -				    in->variantType == YAFFS_OBJECT_TYPE_FILE -				    && chunkBase < -				    in->variant.fileVariant.shrinkSize) { -					/* This has not been invalidated by a resize */ -					if(!yaffs_PutChunkIntoFile(in, tags.chunkId, -							       chunk, -1)){ -						alloc_failed = 1; -					} +	struct yaffs_obj *obj; +	struct yaffs_obj *parent; +	int i; +	struct list_head *lh; +	struct list_head *n; +	int depth_limit; +	int hanging; -					/* 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 = -					    (tags.chunkId - -					     1) * dev->nDataBytesPerChunk + -					    tags.byteCount; +	if (dev->read_only) +		return; -					if (!in->valid &&	/* have not got an object header yet */ -					    in->variant.fileVariant. -					    scannedFileSize < endpos) { -						in->variant.fileVariant. -						    scannedFileSize = endpos; -						in->variant.fileVariant. -						    fileSize = -						    in->variant.fileVariant. -						    scannedFileSize; -					} +	/* Iterate through the objects in each hash entry, +	 * looking at each object. +	 * Make sure it is rooted. +	 */ -				} else if(in) { -					/* This chunk has been invalidated by a resize, so delete */ -					yaffs_DeleteChunk(dev, chunk, 1, __LINE__); +	for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { +		list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { +			obj = list_entry(lh, struct yaffs_obj, hash_link); +			parent = obj->parent; -				} +			if (yaffs_has_null_parent(dev, obj)) { +				/* These directories are not hanging */ +				hanging = 0; +			} else if (!parent || +				   parent->variant_type != +				   YAFFS_OBJECT_TYPE_DIRECTORY) { +				hanging = 1; +			} else if (yaffs_has_null_parent(dev, parent)) { +				hanging = 0;  			} else { -				/* chunkId == 0, so it is an ObjectHeader. -				 * Thus, we read in the object header and make the object +				/* +				 * Need to follow the parent chain to +				 * see if it is hanging.  				 */ -				foundChunksInBlock = 1; - -				yaffs_SetChunkBit(dev, blk, c); -				bi->pagesInUse++; - -				oh = NULL; -				in = NULL; - -				if (tags.extraHeaderInfoAvailable) { -					in = yaffs_FindOrCreateObjectByNumber -					    (dev, tags.objectId, -					     tags.extraObjectType); -				} - -				if (!in || -#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD -				    !in->valid || -#endif -				    tags.extraShadows || -				    (!in->valid && -				    (tags.objectId == YAFFS_OBJECTID_ROOT || -				     tags.objectId == 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_ReadChunkWithTagsFromNAND(dev, -									chunk, -									chunkData, -									NULL); - -					oh = (yaffs_ObjectHeader *) chunkData; - -					if (!in) -						in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type); - -				} - -				if (!in) { -					/* TODO Hoosterman we have a problem! */ -					T(YAFFS_TRACE_ERROR, -					  (TSTR -					   ("yaffs tragedy: Could not make object for object  %d  " -					    "at chunk %d during scan" -					    TENDSTR), tags.objectId, chunk)); - -				} - -				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->variantType == YAFFS_OBJECT_TYPE_FILE) && -					     ((oh && -					       oh-> type == YAFFS_OBJECT_TYPE_FILE)|| -					      (tags.extraHeaderInfoAvailable  && -					       tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE)) -					    ) { -						__u32 thisSize = -						    (oh) ? oh->fileSize : tags. -						    extraFileLength; -						__u32 parentObjectId = -						    (oh) ? oh-> -						    parentObjectId : tags. -						    extraParentObjectId; -						unsigned isShrink = -						    (oh) ? oh->isShrink : tags. -						    extraIsShrinkHeader; - -						/* If it is deleted (unlinked at start also means deleted) -						 * we treat the file size as being zeroed at this point. -						 */ -						if (parentObjectId == -						    YAFFS_OBJECTID_DELETED -						    || parentObjectId == -						    YAFFS_OBJECTID_UNLINKED) { -							thisSize = 0; -							isShrink = 1; -						} - -						if (isShrink && -						    in->variant.fileVariant. -						    shrinkSize > thisSize) { -							in->variant.fileVariant. -							    shrinkSize = -							    thisSize; -						} - -						if (isShrink) { -							bi->hasShrinkHeader = 1; -						} - -					} -					/* Use existing - destroy this one. */ -					yaffs_DeleteChunk(dev, chunk, 1, __LINE__); - -				} - -				if (!in->valid && -				    (tags.objectId == YAFFS_OBJECTID_ROOT || -				     tags.objectId == -				     YAFFS_OBJECTID_LOSTNFOUND)) { -					/* We only load some info, don't fiddle with directory structure */ -					in->valid = 1; - -					if(oh) { -						in->variantType = oh->type; - -						in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE -						in->win_atime[0] = oh->win_atime[0]; -						in->win_ctime[0] = oh->win_ctime[0]; -						in->win_mtime[0] = oh->win_mtime[0]; -						in->win_atime[1] = oh->win_atime[1]; -						in->win_ctime[1] = oh->win_ctime[1]; -						in->win_mtime[1] = oh->win_mtime[1]; -#else -						in->yst_uid = oh->yst_uid; -						in->yst_gid = oh->yst_gid; -						in->yst_atime = oh->yst_atime; -						in->yst_mtime = oh->yst_mtime; -						in->yst_ctime = oh->yst_ctime; -						in->yst_rdev = oh->yst_rdev; - -#endif -					} else { -						in->variantType = tags.extraObjectType; -						in->lazyLoaded = 1; -					} - -					in->chunkId = chunk; - -				} else if (!in->valid) { -					/* we need to load this info */ - -					in->valid = 1; -					in->chunkId = chunk; - -					if(oh) { -						in->variantType = oh->type; - -						in->yst_mode = oh->yst_mode; -#ifdef CONFIG_YAFFS_WINCE -						in->win_atime[0] = oh->win_atime[0]; -						in->win_ctime[0] = oh->win_ctime[0]; -						in->win_mtime[0] = oh->win_mtime[0]; -						in->win_atime[1] = oh->win_atime[1]; -						in->win_ctime[1] = oh->win_ctime[1]; -						in->win_mtime[1] = oh->win_mtime[1]; -#else -						in->yst_uid = oh->yst_uid; -						in->yst_gid = oh->yst_gid; -						in->yst_atime = oh->yst_atime; -						in->yst_mtime = oh->yst_mtime; -						in->yst_ctime = oh->yst_ctime; -						in->yst_rdev = oh->yst_rdev; -#endif - -						if (oh->shadowsObject > 0) -							yaffs_HandleShadowedObject(dev, -									   oh-> -									   shadowsObject, -									   1); - - -						yaffs_SetObjectName(in, oh->name); -						parent = -						    yaffs_FindOrCreateObjectByNumber -							(dev, oh->parentObjectId, -							 YAFFS_OBJECT_TYPE_DIRECTORY); - -						 fileSize = oh->fileSize; -						 isShrink = oh->isShrink; -						 equivalentObjectId = oh->equivalentObjectId; - -					} -					else { -						in->variantType = tags.extraObjectType; -						parent = -						    yaffs_FindOrCreateObjectByNumber -							(dev, tags.extraParentObjectId, -							 YAFFS_OBJECT_TYPE_DIRECTORY); -						 fileSize = tags.extraFileLength; -						 isShrink = tags.extraIsShrinkHeader; -						 equivalentObjectId = tags.extraEquivalentObjectId; -						in->lazyLoaded = 1; - -					} -					in->dirty = 0; - -					/* directory stuff... -					 * hook up to parent -					 */ - -					if (parent->variantType == -					    YAFFS_OBJECT_TYPE_UNKNOWN) { -						/* Set up as a directory */ -						parent->variantType = -						    YAFFS_OBJECT_TYPE_DIRECTORY; -						INIT_LIST_HEAD(&parent->variant. -							       directoryVariant. -							       children); -					} else if (parent->variantType != -						   YAFFS_OBJECT_TYPE_DIRECTORY) -					{ -						/* Hoosterman, another problem.... -						 * We're trying to use a non-directory as a directory -						 */ - -						T(YAFFS_TRACE_ERROR, -						  (TSTR -						   ("yaffs tragedy: attempting to use non-directory as" -						    " a directory in scan. Put in lost+found." -						    TENDSTR))); -						parent = dev->lostNFoundDir; -					} - -					yaffs_AddObjectToDirectory(parent, in); - -					itsUnlinked = (parent == dev->deletedDir) || -						      (parent == dev->unlinkedDir); - -					if (isShrink) { -						/* Mark the block as having a shrinkHeader */ -						bi->hasShrinkHeader = 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->variantType) { -					case YAFFS_OBJECT_TYPE_UNKNOWN: -						/* Todo got a problem */ -						break; -					case YAFFS_OBJECT_TYPE_FILE: - -						if (in->variant.fileVariant. -						    scannedFileSize < fileSize) { -							/* This covers the case where the file size is greater -							 * than where the data is -							 * This will happen if the file is resized to be larger -							 * than its current data extents. -							 */ -							in->variant.fileVariant.fileSize = fileSize; -							in->variant.fileVariant.scannedFileSize = -							    in->variant.fileVariant.fileSize; -						} - -						if (isShrink && -						    in->variant.fileVariant.shrinkSize > fileSize) { -							in->variant.fileVariant.shrinkSize = fileSize; -						} - -						break; -					case YAFFS_OBJECT_TYPE_HARDLINK: -						if(!itsUnlinked) { -						  in->variant.hardLinkVariant.equivalentObjectId = -						    equivalentObjectId; -						  in->hardLinks.next = -						    (struct list_head *) hardList; -						  hardList = in; -						} -						break; -					case YAFFS_OBJECT_TYPE_DIRECTORY: -						/* Do nothing */ -						break; -					case YAFFS_OBJECT_TYPE_SPECIAL: -						/* Do nothing */ -						break; -					case YAFFS_OBJECT_TYPE_SYMLINK: -						if(oh){ -						   in->variant.symLinkVariant.alias = -						    yaffs_CloneString(oh-> -								      alias); -						   if(!in->variant.symLinkVariant.alias) -							alloc_failed = 1; -						} -						break; -					} +				hanging = 0; +				depth_limit = 100; +				while (parent != dev->root_dir && +				       parent->parent && +				       parent->parent->variant_type == +				       YAFFS_OBJECT_TYPE_DIRECTORY && +				       depth_limit > 0) { +					parent = parent->parent; +					depth_limit--;  				} - +				if (parent != dev->root_dir) +					hanging = 1;  			} - -		} /* End of scanning for each chunk */ - -		if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { -			/* If we got this far while scanning, then the block is fully allocated. */ -			state = YAFFS_BLOCK_STATE_FULL; -		} - -		bi->blockState = state; - -		/* Now let's see if it was dirty */ -		if (bi->pagesInUse == 0 && -		    !bi->hasShrinkHeader && -		    bi->blockState == YAFFS_BLOCK_STATE_FULL) { -			yaffs_BlockBecameDirty(dev, blk); -		} - -	} - -	if (altBlockIndex) -		YFREE_ALT(blockIndex); -	else -		YFREE(blockIndex); - -	/* Ok, we've done all the scanning. -	 * Fix up the hard link chains. -	 * We should now have scanned all the objects, now it's time to add these -	 * hardlinks. -	 */ -	yaffs_HardlinkFixup(dev,hardList); - - -	/* -	*  Sort out state of unlinked and deleted objects. -	*/ -	{ -		struct list_head *i; -		struct list_head *n; - -		yaffs_Object *l; - -		/* Soft delete all the unlinked files */ -		list_for_each_safe(i, n, -				   &dev->unlinkedDir->variant.directoryVariant. -				   children) { -			if (i) { -				l = list_entry(i, yaffs_Object, siblings); -				yaffs_DestroyObject(l); -			} -		} - -		/* Soft delete all the deletedDir files */ -		list_for_each_safe(i, n, -				   &dev->deletedDir->variant.directoryVariant. -				   children) { -			if (i) { -				l = list_entry(i, yaffs_Object, siblings); -				yaffs_DestroyObject(l); - +			if (hanging) { +				yaffs_trace(YAFFS_TRACE_SCAN, +					"Hanging object %d moved to lost and found", +					obj->obj_id); +				yaffs_add_obj_to_dir(dev->lost_n_found, obj);  			}  		}  	} - -	yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); - -	if(alloc_failed){ -		return YAFFS_FAIL; -	} - -	T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR))); - -	return YAFFS_OK;  } -/*------------------------------  Directory Functions ----------------------------- */ - -static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj) +/* + * Delete directory contents for cleaning up lost and found. + */ +static void yaffs_del_dir_contents(struct yaffs_obj *dir)  { -	yaffs_Device *dev = obj->myDev; +	struct yaffs_obj *obj; +	struct list_head *lh; +	struct list_head *n; -	if(dev && dev->removeObjectCallback) -		dev->removeObjectCallback(obj); +	if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) +		BUG(); -	list_del_init(&obj->siblings); -	obj->parent = NULL; +	list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { +		obj = list_entry(lh, struct yaffs_obj, siblings); +		if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) +			yaffs_del_dir_contents(obj); +		yaffs_trace(YAFFS_TRACE_SCAN, +			"Deleting lost_found object %d", +			obj->obj_id); +		yaffs_unlink_obj(obj); +	}  } - -static void yaffs_AddObjectToDirectory(yaffs_Object * directory, -				       yaffs_Object * obj) +static void yaffs_empty_l_n_f(struct yaffs_dev *dev)  { - -	if (!directory) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("tragedy: Trying to add an object to a null pointer directory" -		    TENDSTR))); -		YBUG(); -	} -	if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("tragedy: Trying to add an object to a non-directory" -		    TENDSTR))); -		YBUG(); -	} - -	if (obj->siblings.prev == NULL) { -		/* Not initialised */ -		INIT_LIST_HEAD(&obj->siblings); - -	} else if (!list_empty(&obj->siblings)) { -		/* If it is holed up somewhere else, un hook it */ -		yaffs_RemoveObjectFromDirectory(obj); -	} -	/* Now add it */ -	list_add(&obj->siblings, &directory->variant.directoryVariant.children); -	obj->parent = directory; - -	if (directory == obj->myDev->unlinkedDir -	    || directory == obj->myDev->deletedDir) { -		obj->unlinked = 1; -		obj->myDev->nUnlinkedFiles++; -		obj->renameAllowed = 0; -	} +	yaffs_del_dir_contents(dev->lost_n_found);  } -yaffs_Object *yaffs_FindObjectByName(yaffs_Object * directory, -				     const YCHAR * name) + +struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, +				     const YCHAR *name)  {  	int sum; -  	struct list_head *i;  	YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; +	struct yaffs_obj *l; -	yaffs_Object *l; - -	if (!name) { +	if (!name)  		return NULL; -	}  	if (!directory) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("tragedy: yaffs_FindObjectByName: null pointer directory" -		    TENDSTR))); -		YBUG(); +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"tragedy: yaffs_find_by_name: null pointer directory" +			); +		BUG(); +		return NULL;  	} -	if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); -		YBUG(); +	if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"tragedy: yaffs_find_by_name: non-directory" +			); +		BUG();  	} -	sum = yaffs_CalcNameSum(name); +	sum = yaffs_calc_name_sum(name); -	list_for_each(i, &directory->variant.directoryVariant.children) { -		if (i) { -			l = list_entry(i, yaffs_Object, siblings); +	list_for_each(i, &directory->variant.dir_variant.children) { +		l = list_entry(i, struct yaffs_obj, siblings); -			yaffs_CheckObjectDetailsLoaded(l); +		if (l->parent != directory) +			BUG(); -			/* Special case for lost-n-found */ -			if (l->objectId == YAFFS_OBJECTID_LOSTNFOUND) { -				if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) { -					return l; -				} -			} else if (yaffs_SumCompare(l->sum, sum) || l->chunkId <= 0) -			{ -				/* LostnFound cunk called Objxxx -				 * Do a real check -				 */ -				yaffs_GetObjectName(l, buffer, -						    YAFFS_MAX_NAME_LENGTH); -				if (yaffs_strncmp(name, buffer,YAFFS_MAX_NAME_LENGTH) == 0) { -					return l; -				} +		yaffs_check_obj_details_loaded(l); -			} +		/* Special case for lost-n-found */ +		if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { +			if (!yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME)) +				return l; +		} else if (l->sum == sum || l->hdr_chunk <= 0) { +			/* LostnFound chunk called Objxxx +			 * Do a real check +			 */ +			yaffs_get_obj_name(l, buffer, +				YAFFS_MAX_NAME_LENGTH + 1); +			if (!yaffs_strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) +				return l;  		}  	} -  	return NULL;  } - -#if 0 -int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir, -				   int (*fn) (yaffs_Object *)) -{ -	struct list_head *i; -	yaffs_Object *l; - -	if (!theDir) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("tragedy: yaffs_FindObjectByName: null pointer directory" -		    TENDSTR))); -		YBUG(); -	} -	if (theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); -		YBUG(); -	} - -	list_for_each(i, &theDir->variant.directoryVariant.children) { -		if (i) { -			l = list_entry(i, yaffs_Object, siblings); -			if (l && !fn(l)) { -				return YAFFS_FAIL; -			} -		} -	} - -	return YAFFS_OK; - -} -#endif -  /* GetEquivalentObject dereferences any hard links to get to the   * actual object.   */ -yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj) +struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj)  { -	if (obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { -		/* We want the object id of the equivalent object, not this one */ -		obj = obj->variant.hardLinkVariant.equivalentObject; -		yaffs_CheckObjectDetailsLoaded(obj); +	if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { +		obj = obj->variant.hardlink_variant.equiv_obj; +		yaffs_check_obj_details_loaded(obj);  	}  	return obj; -  } -int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize) -{ -	memset(name, 0, buffSize * sizeof(YCHAR)); - -	yaffs_CheckObjectDetailsLoaded(obj); +/* + *  A note or two on object names. + *  * If the object name is missing, we then make one up in the form objnnn + * + *  * ASCII names are stored in the object header's name field from byte zero + *  * Unicode names are historically stored starting from byte zero. + * + * Then there are automatic Unicode names... + * The purpose of these is to save names in a way that can be read as + * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII + * system to share files. + * + * These automatic unicode are stored slightly differently... + *  - If the name can fit in the ASCII character space then they are saved as + *    ascii names as per above. + *  - If the name needs Unicode then the name is saved in Unicode + *    starting at oh->name[1]. -	if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) { -		yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1); -	} else if (obj->chunkId <= 0) { -		YCHAR locName[20]; + */ +static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, +				int buffer_size) +{ +	/* Create an object name if we could not find one. */ +	if (yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { +		YCHAR local_name[20]; +		YCHAR num_string[20]; +		YCHAR *x = &num_string[19]; +		unsigned v = obj->obj_id; +		num_string[19] = 0; +		while (v > 0) { +			x--; +			*x = '0' + (v % 10); +			v /= 10; +		}  		/* make up a name */ -		yaffs_sprintf(locName, _Y("%s%d"), YAFFS_LOSTNFOUND_PREFIX, -			      obj->objectId); -		yaffs_strncpy(name, locName, buffSize - 1); - -	} -#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM -	else if (obj->shortName[0]) { -		yaffs_strcpy(name, obj->shortName); +		yaffs_strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); +		yaffs_strcat(local_name, x); +		yaffs_strncpy(name, local_name, buffer_size - 1);  	} -#endif -	else { -		__u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__); +} + +int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) +{ +	memset(name, 0, buffer_size * sizeof(YCHAR)); +	yaffs_check_obj_details_loaded(obj); +	if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { +		yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); +	} else if (obj->short_name[0]) { +		yaffs_strcpy(name, obj->short_name); +	} else if (obj->hdr_chunk > 0) { +		int result; +		u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); -		yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *) buffer; +		struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; -		memset(buffer, 0, obj->myDev->nDataBytesPerChunk); +		memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); -		if (obj->chunkId >= 0) { -			yaffs_ReadChunkWithTagsFromNAND(obj->myDev, -							obj->chunkId, buffer, -							NULL); +		if (obj->hdr_chunk > 0) { +			result = yaffs_rd_chunk_tags_nand(obj->my_dev, +							  obj->hdr_chunk, +							  buffer, NULL);  		} -		yaffs_strncpy(name, oh->name, buffSize - 1); +		yaffs_load_name_from_oh(obj->my_dev, name, oh->name, +					buffer_size); -		yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__); +		yaffs_release_temp_buffer(obj->my_dev, buffer);  	} -	return yaffs_strlen(name); +	yaffs_fix_null_name(obj, name, buffer_size); + +	return yaffs_strnlen(name, YAFFS_MAX_NAME_LENGTH);  } -int yaffs_GetObjectFileLength(yaffs_Object * obj) +loff_t yaffs_get_obj_length(struct yaffs_obj *obj)  { -  	/* Dereference any hard linking */ -	obj = yaffs_GetEquivalentObject(obj); +	obj = yaffs_get_equivalent_obj(obj); -	if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { -		return obj->variant.fileVariant.fileSize; -	} -	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { -		return yaffs_strlen(obj->variant.symLinkVariant.alias); +	if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) +		return obj->variant.file_variant.file_size; +	if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { +		if (!obj->variant.symlink_variant.alias) +			return 0; +		return yaffs_strnlen(obj->variant.symlink_variant.alias, +				     YAFFS_MAX_ALIAS_LENGTH);  	} else {  		/* Only a directory should drop through to here */ -		return obj->myDev->nDataBytesPerChunk; +		return obj->my_dev->data_bytes_per_chunk;  	}  } -int yaffs_GetObjectLinkCount(yaffs_Object * obj) +int yaffs_get_obj_link_count(struct yaffs_obj *obj)  {  	int count = 0;  	struct list_head *i; -	if (!obj->unlinked) { +	if (!obj->unlinked)  		count++;	/* the object itself */ -	} -	list_for_each(i, &obj->hardLinks) { -		count++;	/* add the hard links; */ -	} -	return count; +	list_for_each(i, &obj->hard_links) +	    count++;		/* add the hard links; */ + +	return count;  } -int yaffs_GetObjectInode(yaffs_Object * obj) +int yaffs_get_obj_inode(struct yaffs_obj *obj)  { -	obj = yaffs_GetEquivalentObject(obj); +	obj = yaffs_get_equivalent_obj(obj); -	return obj->objectId; +	return obj->obj_id;  } -unsigned yaffs_GetObjectType(yaffs_Object * obj) +unsigned yaffs_get_obj_type(struct yaffs_obj *obj)  { -	obj = yaffs_GetEquivalentObject(obj); +	obj = yaffs_get_equivalent_obj(obj); -	switch (obj->variantType) { +	switch (obj->variant_type) {  	case YAFFS_OBJECT_TYPE_FILE:  		return DT_REG;  		break; @@ -6725,605 +4532,501 @@ unsigned yaffs_GetObjectType(yaffs_Object * obj)  			return DT_BLK;  		if (S_ISSOCK(obj->yst_mode))  			return DT_SOCK; +		return DT_REG; +		break;  	default:  		return DT_REG;  		break;  	}  } -YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj) -{ -	obj = yaffs_GetEquivalentObject(obj); -	if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { -		return yaffs_CloneString(obj->variant.symLinkVariant.alias); -	} else { -		return yaffs_CloneString(_Y("")); -	} -} - -#ifndef CONFIG_YAFFS_WINCE - -int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr) -{ -	unsigned int valid = attr->ia_valid; - -	if (valid & ATTR_MODE) -		obj->yst_mode = attr->ia_mode; -	if (valid & ATTR_UID) -		obj->yst_uid = attr->ia_uid; -	if (valid & ATTR_GID) -		obj->yst_gid = attr->ia_gid; - -	if (valid & ATTR_ATIME) -		obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); -	if (valid & ATTR_CTIME) -		obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); -	if (valid & ATTR_MTIME) -		obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); - -	if (valid & ATTR_SIZE) -		yaffs_ResizeFile(obj, attr->ia_size); - -	yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0); - -	return YAFFS_OK; - -} -int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr) +YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj)  { -	unsigned int valid = 0; - -	attr->ia_mode = obj->yst_mode; -	valid |= ATTR_MODE; -	attr->ia_uid = obj->yst_uid; -	valid |= ATTR_UID; -	attr->ia_gid = obj->yst_gid; -	valid |= ATTR_GID; - -	Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; -	valid |= ATTR_ATIME; -	Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; -	valid |= ATTR_CTIME; -	Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; -	valid |= ATTR_MTIME; - -	attr->ia_size = yaffs_GetFileSize(obj); -	valid |= ATTR_SIZE; - -	attr->ia_valid = valid; - -	return YAFFS_OK; - -} - -#endif - -#if 0 -int yaffs_DumpObject(yaffs_Object * obj) -{ -	YCHAR name[257]; - -	yaffs_GetObjectName(obj, name, 256); - -	T(YAFFS_TRACE_ALWAYS, -	  (TSTR -	   ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d" -	    " chunk %d type %d size %d\n" -	    TENDSTR), obj->objectId, yaffs_GetObjectInode(obj), name, -	   obj->dirty, obj->valid, obj->serial, obj->sum, obj->chunkId, -	   yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj))); - -	return YAFFS_OK; +	obj = yaffs_get_equivalent_obj(obj); +	if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) +		return yaffs_clone_str(obj->variant.symlink_variant.alias); +	else +		return yaffs_clone_str(_Y(""));  } -#endif -/*---------------------------- Initialisation code -------------------------------------- */ +/*--------------------------- Initialisation code -------------------------- */ -static int yaffs_CheckDevFunctions(const yaffs_Device * dev) +static int yaffs_check_dev_fns(const struct yaffs_dev *dev)  { -  	/* Common functions, gotta have */ -	if (!dev->eraseBlockInNAND || !dev->initialiseNAND) +	if (!dev->param.erase_fn || !dev->param.initialise_flash_fn)  		return 0; -#ifdef CONFIG_YAFFS_YAFFS2 -  	/* Can use the "with tags" style interface for yaffs1 or yaffs2 */ -	if (dev->writeChunkWithTagsToNAND && -	    dev->readChunkWithTagsFromNAND && -	    !dev->writeChunkToNAND && -	    !dev->readChunkFromNAND && -	    dev->markNANDBlockBad && dev->queryNANDBlock) +	if (dev->param.write_chunk_tags_fn && +	    dev->param.read_chunk_tags_fn && +	    !dev->param.write_chunk_fn && +	    !dev->param.read_chunk_fn && +	    dev->param.bad_block_fn && dev->param.query_block_fn)  		return 1; -#endif  	/* Can use the "spare" style interface for yaffs1 */ -	if (!dev->isYaffs2 && -	    !dev->writeChunkWithTagsToNAND && -	    !dev->readChunkWithTagsFromNAND && -	    dev->writeChunkToNAND && -	    dev->readChunkFromNAND && -	    !dev->markNANDBlockBad && !dev->queryNANDBlock) +	if (!dev->param.is_yaffs2 && +	    !dev->param.write_chunk_tags_fn && +	    !dev->param.read_chunk_tags_fn && +	    dev->param.write_chunk_fn && +	    dev->param.read_chunk_fn && +	    !dev->param.bad_block_fn && !dev->param.query_block_fn)  		return 1;  	return 0;		/* bad */  } - -static int yaffs_CreateInitialDirectories(yaffs_Device *dev) +static int yaffs_create_initial_dir(struct yaffs_dev *dev)  { -	/* Initialise the unlinked, deleted, root and lost and found directories */ - -	dev->lostNFoundDir = dev->rootDir =  NULL; -	dev->unlinkedDir = dev->deletedDir = NULL; +	/* Initialise the unlinked, deleted, root and lost+found directories */ +	dev->lost_n_found = dev->root_dir = NULL; +	dev->unlinked_dir = dev->del_dir = NULL; +	dev->unlinked_dir = +	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); +	dev->del_dir = +	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); +	dev->root_dir = +	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, +				  YAFFS_ROOT_MODE | S_IFDIR); +	dev->lost_n_found = +	    yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, +				  YAFFS_LOSTNFOUND_MODE | S_IFDIR); -	dev->unlinkedDir = -	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); - -	dev->deletedDir = -	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); - -	dev->rootDir = -	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT, -				      YAFFS_ROOT_MODE | S_IFDIR); -	dev->lostNFoundDir = -	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND, -				      YAFFS_LOSTNFOUND_MODE | S_IFDIR); - -	if(dev->lostNFoundDir && dev->rootDir && dev->unlinkedDir && dev->deletedDir){ -		yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); +	if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir +	    && dev->del_dir) { +		yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found);  		return YAFFS_OK;  	} -  	return YAFFS_FAIL;  } -int yaffs_GutsInitialise(yaffs_Device * dev) +int yaffs_guts_initialise(struct yaffs_dev *dev)  {  	int init_failed = 0;  	unsigned x;  	int bits; -	T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR))); +	yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_guts_initialise()");  	/* Check stuff that must be set */  	if (!dev) { -		T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Need a device" TENDSTR))); +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"yaffs: Need a device" +			);  		return YAFFS_FAIL;  	} -	dev->internalStartBlock = dev->startBlock; -	dev->internalEndBlock = dev->endBlock; -	dev->blockOffset = 0; -	dev->chunkOffset = 0; -	dev->nFreeChunks = 0; +	if (dev->is_mounted) { +		yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); +		return YAFFS_FAIL; +	} + +	dev->internal_start_block = dev->param.start_block; +	dev->internal_end_block = dev->param.end_block; +	dev->block_offset = 0; +	dev->chunk_offset = 0; +	dev->n_free_chunks = 0; -	if (dev->startBlock == 0) { -		dev->internalStartBlock = dev->startBlock + 1; -		dev->internalEndBlock = dev->endBlock + 1; -		dev->blockOffset = 1; -		dev->chunkOffset = dev->nChunksPerBlock; +	dev->gc_block = 0; + +	if (dev->param.start_block == 0) { +		dev->internal_start_block = dev->param.start_block + 1; +		dev->internal_end_block = dev->param.end_block + 1; +		dev->block_offset = 1; +		dev->chunk_offset = dev->param.chunks_per_block;  	}  	/* Check geometry parameters. */ -	if ((dev->isYaffs2 && dev->nDataBytesPerChunk < 1024) || -	    (!dev->isYaffs2 && dev->nDataBytesPerChunk != 512) || -	     dev->nChunksPerBlock < 2 || -	     dev->nReservedBlocks < 2 || -	     dev->internalStartBlock <= 0 || -	     dev->internalEndBlock <= 0 || -	     dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2)	// otherwise it is too small -	    ) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s " -		    TENDSTR), dev->nDataBytesPerChunk, dev->isYaffs2 ? "2" : "")); +	if ((!dev->param.inband_tags && dev->param.is_yaffs2 && +		dev->param.total_bytes_per_chunk < 1024) || +		(!dev->param.is_yaffs2 && +			dev->param.total_bytes_per_chunk < 512) || +		(dev->param.inband_tags && !dev->param.is_yaffs2) || +		 dev->param.chunks_per_block < 2 || +		 dev->param.n_reserved_blocks < 2 || +		dev->internal_start_block <= 0 || +		dev->internal_end_block <= 0 || +		dev->internal_end_block <= +		(dev->internal_start_block + dev->param.n_reserved_blocks + 2) +		) { +		/* otherwise it is too small */ +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", +			dev->param.total_bytes_per_chunk, +			dev->param.is_yaffs2 ? "2" : "", +			dev->param.inband_tags);  		return YAFFS_FAIL;  	} -	if (yaffs_InitialiseNAND(dev) != YAFFS_OK) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR("yaffs: InitialiseNAND failed" TENDSTR))); +	if (yaffs_init_nand(dev) != YAFFS_OK) { +		yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed");  		return YAFFS_FAIL;  	} +	/* Sort out space for inband tags, if required */ +	if (dev->param.inband_tags) +		dev->data_bytes_per_chunk = +		    dev->param.total_bytes_per_chunk - +		    sizeof(struct yaffs_packed_tags2_tags_only); +	else +		dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; +  	/* Got the right mix of functions? */ -	if (!yaffs_CheckDevFunctions(dev)) { +	if (!yaffs_check_dev_fns(dev)) {  		/* Function missing */ -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR -		   ("yaffs: device function(s) missing or wrong\n" TENDSTR))); +		yaffs_trace(YAFFS_TRACE_ALWAYS, +			"device function(s) missing or wrong");  		return YAFFS_FAIL;  	} -	/* This is really a compilation check. */ -	if (!yaffs_CheckStructures()) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR("yaffs_CheckStructures failed\n" TENDSTR))); -		return YAFFS_FAIL; -	} - -	if (dev->isMounted) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR("yaffs: device already mounted\n" TENDSTR))); -		return YAFFS_FAIL; -	} - -	/* Finished with most checks. One or two more checks happen later on too. */ - -	dev->isMounted = 1; - +	/* Finished with most checks. Further checks happen later on too. */ +	dev->is_mounted = 1;  	/* OK now calculate a few things for the device */  	/*  	 *  Calculate all the chunk size manipulation numbers:  	 */ -	 /* Start off assuming it is a power of 2 */ -	 dev->chunkShift = ShiftDiv(dev->nDataBytesPerChunk); -	 dev->chunkMask = (1<<dev->chunkShift) - 1; - -	 if(dev->nDataBytesPerChunk == (dev->chunkMask + 1)){ -		/* Yes it is a power of 2, disable crumbs */ -		dev->crumbMask = 0; -		dev->crumbShift = 0; -		dev->crumbsPerChunk = 0; -	 } else { -		/* Not a power of 2, use crumbs instead */ -		dev->crumbShift = ShiftDiv(sizeof(yaffs_PackedTags2TagsPart)); -		dev->crumbMask = (1<<dev->crumbShift)-1; -		dev->crumbsPerChunk = dev->nDataBytesPerChunk/(1 << dev->crumbShift); -		dev->chunkShift = 0; -		dev->chunkMask = 0; -	} - +	x = dev->data_bytes_per_chunk; +	/* We always use dev->chunk_shift and dev->chunk_div */ +	dev->chunk_shift = calc_shifts(x); +	x >>= dev->chunk_shift; +	dev->chunk_div = x; +	/* We only use chunk mask if chunk_div is 1 */ +	dev->chunk_mask = (1 << dev->chunk_shift) - 1;  	/* -	 * Calculate chunkGroupBits. -	 * We need to find the next power of 2 > than internalEndBlock +	 * Calculate chunk_grp_bits. +	 * We need to find the next power of 2 > than internal_end_block  	 */ -	x = dev->nChunksPerBlock * (dev->internalEndBlock + 1); +	x = dev->param.chunks_per_block * (dev->internal_end_block + 1); -	bits = ShiftsGE(x); +	bits = calc_shifts_ceiling(x);  	/* Set up tnode width if wide tnodes are enabled. */ -	if(!dev->wideTnodesDisabled){ +	if (!dev->param.wide_tnodes_disabled) {  		/* bits must be even so that we end up with 32-bit words */ -		if(bits & 1) +		if (bits & 1)  			bits++; -		if(bits < 16) -			dev->tnodeWidth = 16; +		if (bits < 16) +			dev->tnode_width = 16;  		else -			dev->tnodeWidth = bits; +			dev->tnode_width = bits; +	} else { +		dev->tnode_width = 16;  	} -	else -		dev->tnodeWidth = 16; -	dev->tnodeMask = (1<<dev->tnodeWidth)-1; +	dev->tnode_mask = (1 << dev->tnode_width) - 1;  	/* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),  	 * so if the bitwidth of the  	 * chunk range we're using is greater than 16 we need -	 * to figure out chunk shift and chunkGroupSize +	 * to figure out chunk shift and chunk_grp_size  	 */ -	if (bits <= dev->tnodeWidth) -		dev->chunkGroupBits = 0; +	if (bits <= dev->tnode_width) +		dev->chunk_grp_bits = 0;  	else -		dev->chunkGroupBits = bits - dev->tnodeWidth; +		dev->chunk_grp_bits = bits - dev->tnode_width; +	dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; +	if (dev->tnode_size < sizeof(struct yaffs_tnode)) +		dev->tnode_size = sizeof(struct yaffs_tnode); -	dev->chunkGroupSize = 1 << dev->chunkGroupBits; +	dev->chunk_grp_size = 1 << dev->chunk_grp_bits; -	if (dev->nChunksPerBlock < dev->chunkGroupSize) { +	if (dev->param.chunks_per_block < dev->chunk_grp_size) {  		/* We have a problem because the soft delete won't work if  		 * the chunk group size > chunks per block.  		 * This can be remedied by using larger "virtual blocks".  		 */ -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR("yaffs: chunk group too large\n" TENDSTR))); +		yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large");  		return YAFFS_FAIL;  	} -	/* OK, we've finished verifying the device, lets continue with initialisation */ +	/* Finished verifying the device, continue with initialisation */  	/* More device initialisation */ -	dev->garbageCollections = 0; -	dev->passiveGarbageCollections = 0; -	dev->currentDirtyChecker = 0; -	dev->bufferedBlock = -1; -	dev->doingBufferedBlockRewrite = 0; -	dev->nDeletedFiles = 0; -	dev->nBackgroundDeletions = 0; -	dev->nUnlinkedFiles = 0; -	dev->eccFixed = 0; -	dev->eccUnfixed = 0; -	dev->tagsEccFixed = 0; -	dev->tagsEccUnfixed = 0; -	dev->nErasureFailures = 0; -	dev->nErasedBlocks = 0; -	dev->isDoingGC = 0; -	dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */ +	dev->all_gcs = 0; +	dev->passive_gc_count = 0; +	dev->oldest_dirty_gc_count = 0; +	dev->bg_gcs = 0; +	dev->gc_block_finder = 0; +	dev->buffered_block = -1; +	dev->doing_buffered_block_rewrite = 0; +	dev->n_deleted_files = 0; +	dev->n_bg_deletions = 0; +	dev->n_unlinked_files = 0; +	dev->n_ecc_fixed = 0; +	dev->n_ecc_unfixed = 0; +	dev->n_tags_ecc_fixed = 0; +	dev->n_tags_ecc_unfixed = 0; +	dev->n_erase_failures = 0; +	dev->n_erased_blocks = 0; +	dev->gc_disable = 0; +	dev->has_pending_prioritised_gc = 1; +		/* Assume the worst for now, will get fixed on first GC */ +	INIT_LIST_HEAD(&dev->dirty_dirs); +	dev->oldest_dirty_seq = 0; +	dev->oldest_dirty_block = 0;  	/* Initialise temporary buffers and caches. */ -	if(!yaffs_InitialiseTempBuffers(dev)) +	if (!yaffs_init_tmp_buffers(dev))  		init_failed = 1; -	dev->srCache = NULL; -	dev->gcCleanupList = NULL; - +	dev->cache = NULL; +	dev->gc_cleanup_list = NULL; -	if (!init_failed && -	    dev->nShortOpCaches > 0) { +	if (!init_failed && dev->param.n_caches > 0) {  		int i; -		__u8 *buf; -		int srCacheBytes = dev->nShortOpCaches * sizeof(yaffs_ChunkCache); +		void *buf; +		int cache_bytes = +		    dev->param.n_caches * sizeof(struct yaffs_cache); -		if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) { -			dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES; -		} +		if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) +			dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; + +		dev->cache = kmalloc(cache_bytes, GFP_NOFS); -		dev->srCache = YMALLOC(srCacheBytes); -		buf = (__u8 *)dev->srCache; +		buf = (u8 *) dev->cache; -		if(dev->srCache) -			memset(dev->srCache,0,srCacheBytes); +		if (dev->cache) +			memset(dev->cache, 0, cache_bytes); -		for (i = 0; i < dev->nShortOpCaches && buf; i++) { -			dev->srCache[i].object = NULL; -			dev->srCache[i].lastUse = 0; -			dev->srCache[i].dirty = 0; -			dev->srCache[i].data = buf = YMALLOC_DMA(dev->nDataBytesPerChunk); +		for (i = 0; i < dev->param.n_caches && buf; i++) { +			dev->cache[i].object = NULL; +			dev->cache[i].last_use = 0; +			dev->cache[i].dirty = 0; +			dev->cache[i].data = buf = +			    kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);  		} -		if(!buf) +		if (!buf)  			init_failed = 1; -		dev->srLastUse = 0; +		dev->cache_last_use = 0;  	} -	dev->cacheHits = 0; +	dev->cache_hits = 0; -	if(!init_failed){ -		dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); -		if(!dev->gcCleanupList) +	if (!init_failed) { +		dev->gc_cleanup_list = +		    kmalloc(dev->param.chunks_per_block * sizeof(u32), +					GFP_NOFS); +		if (!dev->gc_cleanup_list)  			init_failed = 1;  	} -	if (dev->isYaffs2) { -		dev->useHeaderFileSize = 1; -	} -	if(!init_failed && !yaffs_InitialiseBlocks(dev)) +	if (dev->param.is_yaffs2) +		dev->param.use_header_file_size = 1; + +	if (!init_failed && !yaffs_init_blocks(dev))  		init_failed = 1; -	yaffs_InitialiseTnodes(dev); -	yaffs_InitialiseObjects(dev); +	yaffs_init_tnodes_and_objs(dev); -	if(!init_failed && !yaffs_CreateInitialDirectories(dev)) +	if (!init_failed && !yaffs_create_initial_dir(dev))  		init_failed = 1; +	if (!init_failed && dev->param.is_yaffs2 && +		!dev->param.disable_summary && +		!yaffs_summary_init(dev)) +		init_failed = 1; -	if(!init_failed){ +	if (!init_failed) {  		/* Now scan the flash. */ -		if (dev->isYaffs2) { -			if(yaffs_CheckpointRestore(dev)) { -				T(YAFFS_TRACE_ALWAYS, -				  (TSTR("yaffs: restored from checkpoint" TENDSTR))); +		if (dev->param.is_yaffs2) { +			if (yaffs2_checkpt_restore(dev)) { +				yaffs_check_obj_details_loaded(dev->root_dir); +				yaffs_trace(YAFFS_TRACE_CHECKPOINT | +					YAFFS_TRACE_MOUNT, +					"yaffs: restored from checkpoint" +					);  			} else { -				/* Clean up the mess caused by an aborted checkpoint load -				 * and scan backwards. +				/* Clean up the mess caused by an aborted +				 * checkpoint load then scan backwards.  				 */ -				yaffs_DeinitialiseBlocks(dev); -				yaffs_DeinitialiseTnodes(dev); -				yaffs_DeinitialiseObjects(dev); +				yaffs_deinit_blocks(dev); +				yaffs_deinit_tnodes_and_objs(dev); -				dev->nErasedBlocks = 0; -				dev->nFreeChunks = 0; -				dev->allocationBlock = -1; -				dev->allocationPage = -1; -				dev->nDeletedFiles = 0; -				dev->nUnlinkedFiles = 0; -				dev->nBackgroundDeletions = 0; -				dev->oldestDirtySequence = 0; +				dev->n_erased_blocks = 0; +				dev->n_free_chunks = 0; +				dev->alloc_block = -1; +				dev->alloc_page = -1; +				dev->n_deleted_files = 0; +				dev->n_unlinked_files = 0; +				dev->n_bg_deletions = 0; -				if(!init_failed && !yaffs_InitialiseBlocks(dev)) +				if (!init_failed && !yaffs_init_blocks(dev))  					init_failed = 1; -				yaffs_InitialiseTnodes(dev); -				yaffs_InitialiseObjects(dev); +				yaffs_init_tnodes_and_objs(dev); -				if(!init_failed && !yaffs_CreateInitialDirectories(dev)) +				if (!init_failed +				    && !yaffs_create_initial_dir(dev))  					init_failed = 1; -				if(!init_failed && !yaffs_ScanBackwards(dev)) +				if (!init_failed && !yaffs2_scan_backwards(dev))  					init_failed = 1;  			} -		}else -			if(!yaffs_Scan(dev)) -				init_failed = 1; +		} else if (!yaffs1_scan(dev)) { +			init_failed = 1; +		} + +		yaffs_strip_deleted_objs(dev); +		yaffs_fix_hanging_objs(dev); +		if (dev->param.empty_lost_n_found) +			yaffs_empty_l_n_f(dev);  	} -	if(init_failed){ +	if (init_failed) {  		/* Clean up the mess */ -		T(YAFFS_TRACE_TRACING, -		  (TSTR("yaffs: yaffs_GutsInitialise() aborted.\n" TENDSTR))); +		yaffs_trace(YAFFS_TRACE_TRACING, +		  "yaffs: yaffs_guts_initialise() aborted."); -		yaffs_Deinitialise(dev); +		yaffs_deinitialise(dev);  		return YAFFS_FAIL;  	}  	/* Zero out stats */ -	dev->nPageReads = 0; -	dev->nPageWrites = 0; -	dev->nBlockErasures = 0; -	dev->nGCCopies = 0; -	dev->nRetriedWrites = 0; +	dev->n_page_reads = 0; +	dev->n_page_writes = 0; +	dev->n_erasures = 0; +	dev->n_gc_copies = 0; +	dev->n_retried_writes = 0; -	dev->nRetiredBlocks = 0; +	dev->n_retired_blocks = 0; -	yaffs_VerifyFreeChunks(dev); -	yaffs_VerifyBlocks(dev); +	yaffs_verify_free_chunks(dev); +	yaffs_verify_blocks(dev); +	/* Clean up any aborted checkpoint data */ +	if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) +		yaffs2_checkpt_invalidate(dev); -	T(YAFFS_TRACE_TRACING, -	  (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR))); +	yaffs_trace(YAFFS_TRACE_TRACING, +	  "yaffs: yaffs_guts_initialise() done.");  	return YAFFS_OK; -  } -void yaffs_Deinitialise(yaffs_Device * dev) +void yaffs_deinitialise(struct yaffs_dev *dev)  { -	if (dev->isMounted) { +	if (dev->is_mounted) {  		int i; -		yaffs_DeinitialiseBlocks(dev); -		yaffs_DeinitialiseTnodes(dev); -		yaffs_DeinitialiseObjects(dev); -		if (dev->nShortOpCaches > 0 && -		    dev->srCache) { +		yaffs_deinit_blocks(dev); +		yaffs_deinit_tnodes_and_objs(dev); +		yaffs_summary_deinit(dev); -			for (i = 0; i < dev->nShortOpCaches; i++) { -				if(dev->srCache[i].data) -					YFREE(dev->srCache[i].data); -				dev->srCache[i].data = NULL; +		if (dev->param.n_caches > 0 && dev->cache) { + +			for (i = 0; i < dev->param.n_caches; i++) { +				kfree(dev->cache[i].data); +				dev->cache[i].data = NULL;  			} -			YFREE(dev->srCache); -			dev->srCache = NULL; +			kfree(dev->cache); +			dev->cache = NULL;  		} -		YFREE(dev->gcCleanupList); +		kfree(dev->gc_cleanup_list); -		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -			YFREE(dev->tempBuffer[i].buffer); -		} +		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) +			kfree(dev->temp_buffer[i].buffer); -		dev->isMounted = 0; -	} +		dev->is_mounted = 0; +		if (dev->param.deinitialise_flash_fn) +			dev->param.deinitialise_flash_fn(dev); +	}  } -static int yaffs_CountFreeChunks(yaffs_Device * dev) +int yaffs_count_free_chunks(struct yaffs_dev *dev)  { -	int nFree; +	int n_free = 0;  	int b; +	struct yaffs_block_info *blk; -	yaffs_BlockInfo *blk; - -	for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; -	     b++) { -		blk = yaffs_GetBlockInfo(dev, b); - -		switch (blk->blockState) { +	blk = dev->block_info; +	for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { +		switch (blk->block_state) {  		case YAFFS_BLOCK_STATE_EMPTY:  		case YAFFS_BLOCK_STATE_ALLOCATING:  		case YAFFS_BLOCK_STATE_COLLECTING:  		case YAFFS_BLOCK_STATE_FULL: -			nFree += -			    (dev->nChunksPerBlock - blk->pagesInUse + -			     blk->softDeletions); +			n_free += +			    (dev->param.chunks_per_block - blk->pages_in_use + +			     blk->soft_del_pages);  			break;  		default:  			break;  		} - +		blk++;  	} - -	return nFree; +	return n_free;  } -int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev) +int yaffs_get_n_free_chunks(struct yaffs_dev *dev)  {  	/* This is what we report to the outside world */ +	int n_free; +	int n_dirty_caches; +	int blocks_for_checkpt; +	int i; -	int nFree; -	int nDirtyCacheChunks; -	int blocksForCheckpoint; - -#if 1 -	nFree = dev->nFreeChunks; -#else -	nFree = yaffs_CountFreeChunks(dev); -#endif - -	nFree += dev->nDeletedFiles; +	n_free = dev->n_free_chunks; +	n_free += dev->n_deleted_files; -	/* Now count the number of dirty chunks in the cache and subtract those */ +	/* Now count and subtract the number of dirty chunks in the cache. */ -	{ -		int i; -		for (nDirtyCacheChunks = 0, i = 0; i < dev->nShortOpCaches; i++) { -			if (dev->srCache[i].dirty) -				nDirtyCacheChunks++; -		} +	for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { +		if (dev->cache[i].dirty) +			n_dirty_caches++;  	} -	nFree -= nDirtyCacheChunks; - -	nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock); +	n_free -= n_dirty_caches; -	/* Now we figure out how much to reserve for the checkpoint and report that... */ -	blocksForCheckpoint = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; -	if(blocksForCheckpoint < 0) -		blocksForCheckpoint = 0; +	n_free -= +	    ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); -	nFree -= (blocksForCheckpoint * dev->nChunksPerBlock); +	/* Now figure checkpoint space and report that... */ +	blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); -	if (nFree < 0) -		nFree = 0; +	n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); -	return nFree; +	if (n_free < 0) +		n_free = 0; +	return n_free;  } -static int yaffs_freeVerificationFailures; - -static void yaffs_VerifyFreeChunks(yaffs_Device * dev) +/*\ + * Marshalling functions to get loff_t file sizes into aand out of + * object headers. + */ +void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize)  { -	int counted; -	int difference; - -	if(yaffs_SkipVerification(dev)) -		return; - -	counted = yaffs_CountFreeChunks(dev); - -	difference = dev->nFreeChunks - counted; - -	if (difference) { -		T(YAFFS_TRACE_ALWAYS, -		  (TSTR("Freechunks verification failure %d %d %d" TENDSTR), -		   dev->nFreeChunks, counted, difference)); -		yaffs_freeVerificationFailures++; -	} +	oh->file_size_low = (fsize & 0xFFFFFFFF); +	oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF);  } -/*---------------------------------------- YAFFS test code ----------------------*/ - -#define yaffs_CheckStruct(structure,syze, name) \ -	   if(sizeof(structure) != syze) \ -	       { \ -		 T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),\ -		 name,syze,sizeof(structure))); \ -		 return YAFFS_FAIL; \ -		} - -static int yaffs_CheckStructures(void) +loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh)  { -/*      yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") */ -/*      yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") */ -/*      yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") */ -#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG -	yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode") -#endif -	    yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader") +	loff_t retval; + +	if (~(oh->file_size_high)) +		retval = (((loff_t) oh->file_size_high) << 32) | +			(((loff_t) oh->file_size_low) & 0xFFFFFFFF); +	else +		retval = (loff_t) oh->file_size_low; -	    return YAFFS_OK; +	return retval;  } |