X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_guts.c;h=c29c1f990cf0a0e5cfa1e322a2366630d52f0ac3;hp=db476089e44d59e72a6e0bfb46a6e7a498721b5e;hb=ea4200a7a79a0b43509f9267292aadbf16fba2a9;hpb=a8e9cfa2968a5f16ff9ae578cc1d3202ebef8725 diff --git a/yaffs_guts.c b/yaffs_guts.c index db47608..c29c1f9 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -1,7 +1,7 @@ /* - * YAFFS: Yet another FFS. A NAND-flash specific file system. + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. * - * Copyright (C) 2002 Aleph One Ltd. + * Copyright (C) 2002-2007 Aleph One Ltd. * for Toby Churchill Ltd and Brightstar Engineering * * Created by Charles Manning @@ -9,11 +9,10 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * */ const char *yaffs_guts_c_version = - "$Id: yaffs_guts.c,v 1.37 2006-09-21 08:13:59 charles Exp $"; + "$Id: yaffs_guts.c,v 1.52 2007-10-16 00:45:05 charles Exp $"; #include "yportenv.h" @@ -22,7 +21,7 @@ const char *yaffs_guts_c_version = #include "yaffs_tagsvalidity.h" #include "yaffs_tagscompat.h" -#ifndef CONFIG_YAFFS_OWN_SORT +#ifndef CONFIG_YAFFS_USE_OWN_SORT #include "yaffs_qsort.h" #endif #include "yaffs_nand.h" @@ -30,6 +29,7 @@ const char *yaffs_guts_c_version = #include "yaffs_checkptrw.h" #include "yaffs_nand.h" +#include "yaffs_packedtags2.h" #ifdef CONFIG_YAFFS_WINCE @@ -97,6 +97,8 @@ static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockIn 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 @@ -108,10 +110,103 @@ static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId); static void yaffs_InvalidateCheckpoint(yaffs_Device *dev); +static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, + yaffs_ExtendedTags * tags); + +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); + + +/* Function to calculate chunk and offset */ + +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); + } + 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(); +} + +/* 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) +{ + int extraBits; + int nShifts; + + nShifts = extraBits = 0; + + while(x>1){ + if(x & 1) extraBits++; + x>>=1; + nShifts++; + } + + if(extraBits) + nShifts++; + + return nShifts; +} + +/* Function to return the number of shifts to get a 1 in bit 0 + */ + +static __u32 ShiftDiv(__u32 x) +{ + int nShifts; + + nShifts = 0; + + if(!x) return 0; + + while( !(x&1)){ + x>>=1; + nShifts++; + } + + return nShifts; +} + + + /* * Temporary buffer manipulations. */ +static int yaffs_InitialiseTempBuffers(yaffs_Device *dev) +{ + int i; + __u8 *buf = (__u8 *)1; + + memset(dev->tempBuffer,0,sizeof(dev->tempBuffer)); + + 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); + } + + return buf ? YAFFS_OK : YAFFS_FAIL; + +} + static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo) { int i, j; @@ -143,7 +238,7 @@ static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo) */ dev->unmanagedTempAllocations++; - return YMALLOC(dev->nBytesPerChunk); + return YMALLOC(dev->nDataBytesPerChunk); } @@ -195,6 +290,8 @@ int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer) return 0; } + + /* * Chunk bitmap manipulations */ @@ -211,6 +308,16 @@ static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk) (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); } +static Y_INLINE void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk) +{ + 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(); + } +} + static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk) { __u8 *blkBits = yaffs_BlockBits(dev, blk); @@ -222,12 +329,16 @@ 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)); } @@ -235,6 +346,8 @@ static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk) static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk) { __u8 *blkBits = yaffs_BlockBits(dev, blk); + yaffs_VerifyChunkBitId(dev,blk,chunk); + return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; } @@ -250,6 +363,491 @@ static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk) 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; +} + +/* + * Verification code + */ + +static int yaffs_SkipVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipFullVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipNANDVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); +} + +static const char * blockStateName[] = { +"Unknown", +"Needs scanning", +"Scanning", +"Empty", +"Allocating", +"Full", +"Dirty", +"Checkpoint", +"Collecting", +"Dead" +}; + +static void yaffs_VerifyBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +{ + 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)); + +} + +static void yaffs_VerifyCollectedBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +{ + yaffs_VerifyBlock(dev,bi,n); + + /* After collection the block should be in the erased state */ + /* TODO: This will need to change if we do partial gc */ + + 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)); + } +} + +static void yaffs_VerifyBlocks(yaffs_Device *dev) +{ + int i; + int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; + int nIllegalBlockStates = 0; + + + if(yaffs_SkipVerification(dev)) + return; + + memset(nBlocksPerState,0,sizeof(nBlocksPerState)); + + + for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + yaffs_VerifyBlock(dev,bi,i); + + if(bi->blockState >=0 && bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES) + nBlocksPerState[bi->blockState]++; + else + nIllegalBlockStates++; + + } + + 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])); + + 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))); + +} + +/* + * 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) +{ + 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)); + + + /* + * Check that the object's parent ids match if parentCheck requested. + * + * Tests do not apply to the root object. + */ + + 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)); + + + 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)); + + + if(tags->objectId > 1 && oh->name[0] == 0) /* Null name */ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header name is NULL"TENDSTR), + obj->objectId)); + + 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)); +} + + + +static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn, + __u32 level, int chunkOffset) +{ + int i; + yaffs_Device *dev = obj->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_VerifyTnodeWorker(obj, + tn->internal[i], + level - 1, + (chunkOffset<objectId; + + chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS; + + for(i = 0; i < YAFFS_NTNODES_LEVEL0; i++){ + __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + + if(theChunk > 0){ + /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */ + yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); + if(tags.objectId != objectId || tags.chunkId != chunkOffset){ + T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), + objectId, chunkOffset, theChunk, + tags.objectId, tags.chunkId)); + } + } + chunkOffset++; + } + } + } + + return ok; + +} + + +static void yaffs_VerifyFile(yaffs_Object *obj) +{ + int requiredTallness; + int actualTallness; + __u32 lastChunk; + __u32 x; + __u32 i; + int ok; + yaffs_Device *dev; + yaffs_ExtendedTags tags; + yaffs_Tnode *tn; + __u32 objectId; + + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + + dev = obj->myDev; + objectId = obj->objectId; + + /* 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++; + } + + 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. + */ + + if(yaffs_SkipNANDVerification(dev)) + return; + + for(i = 1; i <= lastChunk; i++){ + tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant,i); + + 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)); + } + } + } + + } + +} + +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 */ +} + +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; +} + +static void yaffs_VerifyObject(yaffs_Object *obj) +{ + yaffs_Device *dev; + + __u32 chunkMin; + __u32 chunkMax; + + __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; + + 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(chunkIdOk && chunkIsLive &&!yaffs_SkipNANDVerification(dev)) { + yaffs_ExtendedTags tags; + yaffs_ObjectHeader *oh; + __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__); + + oh = (yaffs_ObjectHeader *)buffer; + + yaffs_ReadChunkWithTagsFromNAND(dev, obj->chunkId,buffer, &tags); + + yaffs_VerifyObjectHeader(obj,oh,&tags,1); + + yaffs_ReleaseTempBuffer(dev,buffer,__LINE__); + } + + /* 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)); + } + + /* 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)); + } + + 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; + } + + +} + +static void yaffs_VerifyObjects(yaffs_Device *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 */ @@ -297,14 +895,15 @@ static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, int retval = YAFFS_OK; __u8 *data = yaffs_GetTempBuffer(dev, __LINE__); yaffs_ExtendedTags tags; + int result; - yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags); + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags); if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR) retval = YAFFS_FAIL; - if (!yaffs_CheckFF(data, dev->nBytesPerChunk) || tags.chunkUsed) { + if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) { T(YAFFS_TRACE_NANDACCESS, (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND)); retval = YAFFS_FAIL; @@ -321,80 +920,88 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, yaffs_ExtendedTags * tags, int useReserve) { + int attempts = 0; + int writeOk = 0; int chunk; - int writeOk = 0; - int erasedOk = 1; - int attempts = 0; - yaffs_BlockInfo *bi; - yaffs_InvalidateCheckpoint(dev); do { - chunk = yaffs_AllocateChunk(dev, useReserve,&bi); - - if (chunk >= 0) { - /* First check this chunk is erased, if it needs checking. - * The checking policy (unless forced always on) is as follows: - * 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... - * If the block has been erased, we don't need to check. - * - * However, if the block has been prioritised for gc, then - * we think there might be something odd about this block - * and should continue doing erased checks. - * - * Rationale: - * We should only ever see chunks that have not been erased - * if there was a partially written 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. - */ - + yaffs_BlockInfo *bi = 0; + int erasedOk = 0; + + chunk = yaffs_AllocateChunk(dev, useReserve, &bi); + if (chunk < 0) { + /* no space */ + break; + } + + /* First check this chunk is erased, if it needs + * checking. The checking policy (unless forced + * always on) is as follows: + * + * 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... + * If the block has been erased, we don't need to check. + * + * However, if the block has been prioritised for gc, + * then we think there might be something odd about + * this block and stop using it. + * + * Rationale: We should only ever see chunks that have + * not been erased if there was a partially written + * 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. + */ + 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 = 1; + bi->skipErasedCheck = 0; #endif - if(!bi->skipErasedCheck){ - erasedOk = yaffs_CheckChunkErased(dev, chunk); - if(erasedOk && !bi->gcPrioritise) - bi->skipErasedCheck = 1; - } - - if (!erasedOk) { + 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)); - } else { - writeOk = - yaffs_WriteChunkWithTagsToNAND(dev, chunk, - data, tags); - } - - attempts++; + (TSTR ("**>> yaffs chunk %d was not erased" + TENDSTR), chunk)); - if (writeOk) { - /* - * Copy the data into the robustification buffer. - * NB We do this at the end to prevent duplicates in the case of a write error. - * Todo - */ - yaffs_HandleWriteChunkOk(dev, chunk, data, tags); - - } else { - /* The erased check or write failed */ - yaffs_HandleWriteChunkError(dev, chunk, erasedOk); + /* try another chunk */ + continue; } + bi->skipErasedCheck = 1; + } + + writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, + data, tags); + if (writeOk != YAFFS_OK) { + yaffs_HandleWriteChunkError(dev, chunk, erasedOk); + /* try another chunk */ + continue; } - } while (chunk >= 0 && !writeOk); + /* Copy the data into the robustification buffer */ + yaffs_HandleWriteChunkOk(dev, chunk, data, tags); + + } while (writeOk != YAFFS_OK && + (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); + + if(!writeOk) + chunk = -1; if (attempts > 1) { T(YAFFS_TRACE_ERROR, - (TSTR("**>> yaffs write required %d attempts" TENDSTR), - attempts)); + (TSTR("**>> yaffs write required %d attempts" TENDSTR), + attempts)); + dev->nRetriedWrites += (attempts - 1); } @@ -407,13 +1014,15 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND) { + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); yaffs_InvalidateCheckpoint(dev); yaffs_MarkBlockBad(dev, blockInNAND); - yaffs_GetBlockInfo(dev, blockInNAND)->blockState = - YAFFS_BLOCK_STATE_DEAD; + bi->blockState = YAFFS_BLOCK_STATE_DEAD; + bi->gcPrioritise = 0; + bi->needsRetiring = 0; dev->nRetiredBlocks++; } @@ -434,17 +1043,38 @@ static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, { } +void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi) +{ + if(!bi->gcPrioritise){ + bi->gcPrioritise = 1; + dev->hasPendingPrioritisedGCs = 1; + bi->chunkErrorStrikes ++; + + if(bi->chunkErrorStrikes > 3){ + bi->needsRetiring = 1; /* Too many stikes, so retire this */ + T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR))); + + } + + } +} + static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk) { int blockInNAND = chunkInNAND / dev->nChunksPerBlock; yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); - bi->gcPrioritise = 1; + + yaffs_HandleChunkError(dev,bi); + - if(erasedOk) { + 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)); + + } /* Delete the chunk */ @@ -461,7 +1091,7 @@ static __u16 yaffs_CalcNameSum(const YCHAR * name) YUCHAR *bname = (YUCHAR *) name; if (bname) { - while ((*bname) && (i <= YAFFS_MAX_NAME_LENGTH)) { + while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH/2))) { #ifdef CONFIG_YAFFS_CASE_INSENSITIVE sum += yaffs_toupper(*bname) * i; @@ -569,6 +1199,7 @@ static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes) T(YAFFS_TRACE_ERROR, (TSTR ("yaffs: Could not add tnodes to management list" TENDSTR))); + return YAFFS_FAIL; } else { tnl->tnodes = newTnodes; @@ -693,7 +1324,7 @@ void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsi } } -__u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) +static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) { __u32 *map = (__u32 *)tn; __u32 bitInMap; @@ -1211,8 +1842,13 @@ static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects) /* make these things */ newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); + list = YMALLOC(sizeof(yaffs_ObjectList)); - if (!newObjects) { + 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; @@ -1231,15 +1867,9 @@ static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects) /* Now add this bunch of Objects to a list for freeing up. */ - list = YMALLOC(sizeof(yaffs_ObjectList)); - if (!list) { - T(YAFFS_TRACE_ALLOCATE, - (TSTR("Could not add objects to management list" TENDSTR))); - } else { - list->objects = newObjects; - list->next = dev->allocatedObjectList; - dev->allocatedObjectList = list; - } + list->objects = newObjects; + list->next = dev->allocatedObjectList; + dev->allocatedObjectList = list; return YAFFS_OK; } @@ -1496,12 +2126,25 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, { yaffs_Object *theObject; + yaffs_Tnode *tn; if (number < 0) { number = yaffs_CreateNewObjectNumber(dev); } theObject = yaffs_AllocateEmptyObject(dev); + if(!theObject) + return NULL; + + if(type == YAFFS_OBJECT_TYPE_FILE){ + tn = yaffs_GetTnode(dev); + if(!tn){ + yaffs_FreeObject(theObject); + return NULL; + } + } + + if (theObject) { theObject->fake = 0; @@ -1528,8 +2171,7 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, theObject->variant.fileVariant.scannedFileSize = 0; theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; /* max __u32 */ theObject->variant.fileVariant.topLevel = 0; - theObject->variant.fileVariant.top = - yaffs_GetTnode(dev); + theObject->variant.fileVariant.top = tn; break; case YAFFS_OBJECT_TYPE_DIRECTORY: INIT_LIST_HEAD(&theObject->variant.directoryVariant. @@ -1574,7 +2216,8 @@ static YCHAR *yaffs_CloneString(const YCHAR * str) if (str && *str) { newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR)); - yaffs_strcpy(newStr, str); + if(newStr) + yaffs_strcpy(newStr, str); } return newStr; @@ -1598,6 +2241,7 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, const YCHAR * aliasString, __u32 rdev) { yaffs_Object *in; + YCHAR *str; yaffs_Device *dev = parent->myDev; @@ -1607,6 +2251,16 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, } in = yaffs_CreateNewObject(dev, -1, type); + + if(type == YAFFS_OBJECT_TYPE_SYMLINK){ + str = yaffs_CloneString(aliasString); + if(!str){ + yaffs_FreeObject(in); + return NULL; + } + } + + if (in) { in->chunkId = -1; @@ -1638,8 +2292,7 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, switch (type) { case YAFFS_OBJECT_TYPE_SYMLINK: - in->variant.symLinkVariant.alias = - yaffs_CloneString(aliasString); + in->variant.symLinkVariant.alias = str; break; case YAFFS_OBJECT_TYPE_HARDLINK: in->variant.hardLinkVariant.equivalentObject = @@ -1830,9 +2483,12 @@ static int yaffs_InitialiseBlocks(yaffs_Device * dev) { int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + dev->blockInfo = NULL; + dev->chunkBits = NULL; + dev->allocationBlock = -1; /* force it to get a new one */ - /* Todo we're assuming the malloc will pass. */ + /* 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)); @@ -1840,16 +2496,19 @@ static int yaffs_InitialiseBlocks(yaffs_Device * dev) } else dev->blockInfoAlt = 0; + + if(dev->blockInfo){ - /* 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; + /* 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; } - else - dev->chunkBitsAlt = 0; if (dev->blockInfo && dev->chunkBits) { memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo)); @@ -1863,17 +2522,18 @@ static int yaffs_InitialiseBlocks(yaffs_Device * dev) static void yaffs_DeinitialiseBlocks(yaffs_Device * dev) { - if(dev->blockInfoAlt) + if(dev->blockInfoAlt && dev->blockInfo) YFREE_ALT(dev->blockInfo); - else + else if(dev->blockInfo) YFREE(dev->blockInfo); + dev->blockInfoAlt = 0; dev->blockInfo = NULL; - if(dev->chunkBitsAlt) + if(dev->chunkBitsAlt && dev->chunkBits) YFREE_ALT(dev->chunkBits); - else + else if(dev->chunkBits) YFREE(dev->chunkBits); dev->chunkBitsAlt = 0; dev->chunkBits = NULL; @@ -1915,8 +2575,6 @@ static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev, */ return (bi->sequenceNumber <= dev->oldestDirtySequence); - return 1; - } /* FindDiretiestBlock is used to select the dirtiest block (or close enough) @@ -1932,10 +2590,33 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, int i; int iterations; int dirtiest = -1; - int pagesInUse; - int prioritised; + int pagesInUse = 0; + int prioritised=0; yaffs_BlockInfo *bi; - static int nonAggressiveSkip = 0; + 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); + + 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 */ + } + } + } + + if(!pendingPrioritisedExist) /* None found, so we can clear this */ + dev->hasPendingPrioritisedGCs = 0; + } /* If we're doing aggressive GC then we are happy to take a less-dirty block, and * search harder. @@ -1943,14 +2624,15 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, * block has only a few pages in use. */ - nonAggressiveSkip--; + dev->nonAggressiveSkip--; - if (!aggressive && (nonAggressiveSkip > 0)) { + if (!aggressive && (dev->nonAggressiveSkip > 0)) { return -1; } - pagesInUse = - (aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1; + if(!prioritised) + pagesInUse = + (aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1; if (aggressive) { iterations = @@ -1964,7 +2646,7 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, } } - for (i = 0, prioritised = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) { + for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) { b++; if (b < dev->internalStartBlock || b > dev->internalEndBlock) { b = dev->internalStartBlock; @@ -1987,12 +2669,10 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, #endif if (bi->blockState == YAFFS_BLOCK_STATE_FULL && - (bi->gcPrioritise || (bi->pagesInUse - bi->softDeletions)) < pagesInUse && + (bi->pagesInUse - bi->softDeletions) < pagesInUse && yaffs_BlockNotDisqualifiedFromGC(dev, bi)) { dirtiest = b; pagesInUse = (bi->pagesInUse - bi->softDeletions); - if(bi->gcPrioritise) - prioritised = 1; /* Trick it into selecting this one */ } } @@ -2007,7 +2687,7 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, dev->oldestDirtySequence = 0; if (dirtiest > 0) { - nonAggressiveSkip = 4; + dev->nonAggressiveSkip = 4; } return dirtiest; @@ -2022,6 +2702,11 @@ static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo) /* 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" : "")); + bi->blockState = YAFFS_BLOCK_STATE_DIRTY; if (!bi->needsRetiring) { @@ -2034,7 +2719,8 @@ static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo) } } - if (erasedOk && (yaffs_traceMask & YAFFS_TRACE_ERASE)) { + if (erasedOk && + ((yaffs_traceMask & YAFFS_TRACE_ERASE) || !yaffs_SkipVerification(dev))) { int i; for (i = 0; i < dev->nChunksPerBlock; i++) { if (!yaffs_CheckChunkErased @@ -2212,6 +2898,7 @@ static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) int cleanups = 0; int i; int isCheckpointBlock; + int matchingChunk; int chunksBefore = yaffs_GetErasedChunks(dev); int chunksAfter; @@ -2251,6 +2938,8 @@ static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) } else { __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); + + yaffs_VerifyBlock(dev,bi,block); for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock; chunkInBlock < dev->nChunksPerBlock @@ -2276,12 +2965,28 @@ static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) ("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)); + + } if (!object) { T(YAFFS_TRACE_ERROR, (TSTR - ("page %d in gc has no object " - TENDSTR), oldChunk)); + ("page %d in gc has no object: %d %d %d " + TENDSTR), oldChunk, + tags.objectId, tags.chunkId, tags.byteCount)); } if (object && object->deleted @@ -2338,6 +3043,8 @@ static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) oh->shadowsObject = -1; tags.extraShadows = 0; tags.extraIsShrinkHeader = 0; + + yaffs_VerifyObjectHeader(object,oh,&tags,1); } newChunk = @@ -2394,6 +3101,8 @@ static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) } + yaffs_VerifyCollectedBlock(dev,bi,block); + if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) { T(YAFFS_TRACE_GC, (TSTR @@ -2440,7 +3149,7 @@ static int yaffs_CheckGarbageCollection(yaffs_Device * dev) if(checkpointBlockAdjust < 0) checkpointBlockAdjust = 0; - if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust)) { + if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust + 2)) { /* We need a block soon...*/ aggressive = 1; } else { @@ -2580,7 +3289,7 @@ static int yaffs_CheckFileSanity(yaffs_Object * in) objId = in->objectId; fSize = in->variant.fileVariant.fileSize; nChunks = - (fSize + in->myDev->nBytesPerChunk - 1) / in->myDev->nBytesPerChunk; + (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk; for (chunk = 1; chunk <= nChunks; chunk++) { tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant, @@ -2747,13 +3456,13 @@ static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode, if (chunkInNAND >= 0) { return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND, - buffer, NULL); + 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->nBytesPerChunk); + memset(buffer, 0, in->myDev->nDataBytesPerChunk); return 0; } @@ -2768,11 +3477,18 @@ void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn) if (chunkId <= 0) return; + dev->nDeletions++; block = chunkId / dev->nChunksPerBlock; page = chunkId % dev->nChunksPerBlock; + + if(!yaffs_CheckChunkBit(dev,block,page)) + T(YAFFS_TRACE_VERIFY, + (TSTR("Deleting invalid chunk %d"TENDSTR), + chunkId)); + bi = yaffs_GetBlockInfo(dev, block); T(YAFFS_TRACE_DELETION, @@ -2879,18 +3595,23 @@ int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, int prevChunkId; int retVal = 0; + int result = 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; @@ -2898,12 +3619,15 @@ int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, prevChunkId = in->chunkId; if (prevChunkId >= 0) { - yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId, - buffer, NULL); + result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId, + buffer, &oldTags); + + yaffs_VerifyObjectHeader(in,oh,&oldTags,0); + memcpy(oldName, oh->name, sizeof(oh->name)); } - memset(buffer, 0xFF, dev->nBytesPerChunk); + memset(buffer, 0xFF, dev->nDataBytesPerChunk); oh->type = in->variantType; oh->yst_mode = in->yst_mode; @@ -2933,7 +3657,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, if (name && *name) { memset(oh->name, 0, sizeof(oh->name)); yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); - } else if (prevChunkId) { + } else if (prevChunkId>=0) { memcpy(oh->name, oldName, sizeof(oh->name)); } else { memset(oh->name, 0, sizeof(oh->name)); @@ -2987,6 +3711,8 @@ int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0; newTags.extraObjectType = in->variantType; + yaffs_VerifyObjectHeader(in,oh,&newTags,1); + /* Create new chunk in NAND */ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, @@ -3043,12 +3769,11 @@ static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj) yaffs_ChunkCache *cache; int nCaches = obj->myDev->nShortOpCaches; - if(nCaches > 0){ - for(i = 0; i < nCaches; i++){ - if (dev->srCache[i].object == obj && - dev->srCache[i].dirty) - return 1; - } + for(i = 0; i < nCaches; i++){ + cache = &dev->srCache[i]; + if (cache->object == obj && + cache->dirty) + return 1; } return 0; @@ -3310,9 +4035,12 @@ static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in) static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head) { yaffs_CheckpointValidity cp; + + memset(&cp,0,sizeof(cp)); + cp.structType = sizeof(cp); cp.magic = YAFFS_MAGIC; - cp.version = 1; + cp.version = YAFFS_CHECKPOINT_VERSION; cp.head = (head) ? 1 : 0; return (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp))? @@ -3329,7 +4057,7 @@ static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head) if(ok) ok = (cp.structType == sizeof(cp)) && (cp.magic == YAFFS_MAGIC) && - (cp.version == 1) && + (cp.version == YAFFS_CHECKPOINT_VERSION) && (cp.head == ((head) ? 1 : 0)); return ok ? 1 : 0; } @@ -3546,12 +4274,15 @@ static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) 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)); while(ok && (~baseChunk)){ + nread++; /* Read level 0 tnode */ + /* printf("read tnode at %d\n",baseChunk); */ tn = yaffs_GetTnodeRaw(dev); if(tn) @@ -3565,6 +4296,7 @@ static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) fileStructPtr, baseChunk, tn) ? 1 : 0; + } if(ok) @@ -3572,6 +4304,10 @@ static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) } + T(YAFFS_TRACE_CHECKPOINT,( + TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR), + nread,baseChunk,ok)); + return ok ? 1 : 0; } @@ -3596,7 +4332,11 @@ static int yaffs_WriteCheckpointObjects(yaffs_Device *dev) if (!obj->deferedFree) { yaffs_ObjectToCheckpointObject(&cp,obj); cp.structType = sizeof(cp); - /* printf("Write out object %d type %d\n",obj->objectId,obj->variantType); */ + + 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){ @@ -3628,15 +4368,17 @@ static int yaffs_ReadCheckpointObjects(yaffs_Device *dev) while(ok && !done) { ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); if(cp.structType != sizeof(cp)) { - /* printf("structure parsing failed\n"); */ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("struct size %d instead of %d ok %d"TENDSTR), + cp.structType,sizeof(cp),ok)); ok = 0; } + 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(ok && cp.objectId == ~0) done = 1; else if(ok){ - T(YAFFS_TRACE_CHECKPOINT,(TSTR("Read object %d parent %d type %d" TENDSTR), - cp.objectId,cp.parentId,cp.variantType)); obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType); if(obj) { yaffs_CheckpointObjectToObject(obj,&cp); @@ -3659,22 +4401,76 @@ static int yaffs_ReadCheckpointObjects(yaffs_Device *dev) return ok ? 1 : 0; } -static int yaffs_WriteCheckpointData(yaffs_Device *dev) +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) +{ + __u32 checkpointSum0; + __u32 checkpointSum1; int ok; - ok = yaffs_CheckpointOpen(dev,1); + yaffs_GetCheckpointSum(dev,&checkpointSum0); + + ok = (yaffs_CheckpointRead(dev,&checkpointSum1,sizeof(checkpointSum1)) == sizeof(checkpointSum1)); + + if(!ok) + return 0; + + if(checkpointSum0 != checkpointSum1) + return 0; + + return 1; +} + + +static int yaffs_WriteCheckpointData(yaffs_Device *dev) +{ + + int ok = 1; + 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) + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint device" TENDSTR))); ok = yaffs_WriteCheckpointDevice(dev); - if(ok) + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint objects" TENDSTR))); ok = yaffs_WriteCheckpointObjects(dev); - if(ok) + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); ok = yaffs_WriteCheckpointValidityMarker(dev,0); - + } + + if(ok){ + ok = yaffs_WriteCheckpointSum(dev); + } + + if(!yaffs_CheckpointClose(dev)) ok = 0; @@ -3688,20 +4484,37 @@ static int yaffs_WriteCheckpointData(yaffs_Device *dev) static int yaffs_ReadCheckpointData(yaffs_Device *dev) { - int ok; + int ok = 1; - ok = yaffs_CheckpointOpen(dev,0); /* open for read */ + 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) + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint device" TENDSTR))); ok = yaffs_ReadCheckpointDevice(dev); - if(ok) + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint objects" TENDSTR))); ok = yaffs_ReadCheckpointObjects(dev); - if(ok) + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); ok = yaffs_ReadCheckpointValidityMarker(dev,0); - - + } + + if(ok){ + ok = yaffs_ReadCheckpointSum(dev); + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint checksum %d" TENDSTR),ok)); + } if(!yaffs_CheckpointClose(dev)) ok = 0; @@ -3729,12 +4542,19 @@ static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) int yaffs_CheckpointSave(yaffs_Device *dev) { + T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); - if(!dev->isCheckpointed) + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + + if(!dev->isCheckpointed) { + yaffs_InvalidateCheckpoint(dev); yaffs_WriteCheckpointData(dev); + } - T(YAFFS_TRACE_CHECKPOINT,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + T(YAFFS_TRACE_ALWAYS,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); return dev->isCheckpointed; } @@ -3743,9 +4563,15 @@ int yaffs_CheckpointRestore(yaffs_Device *dev) { int retval; T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); - + retval = yaffs_ReadCheckpointData(dev); + if(dev->isCheckpointed){ + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + } + T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); return retval; @@ -3761,7 +4587,7 @@ 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, __u32 offset, +int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset, int nBytes) { @@ -3777,16 +4603,18 @@ int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, __u32 offset, dev = in->myDev; while (n > 0) { - chunk = offset / dev->nBytesPerChunk + 1; /* The first chunk is 1 */ - start = offset % dev->nBytesPerChunk; + //chunk = offset / dev->nDataBytesPerChunk + 1; + //start = offset % dev->nDataBytesPerChunk; + yaffs_AddrToChunk(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->nBytesPerChunk) { + if ((start + n) < dev->nDataBytesPerChunk) { nToCopy = n; } else { - nToCopy = dev->nBytesPerChunk - start; + nToCopy = dev->nDataBytesPerChunk - start; } cache = yaffs_FindChunkCache(in, chunk); @@ -3795,7 +4623,7 @@ int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, __u32 offset, * then use the cache (if there is caching) * else bypass the cache. */ - if (cache || nToCopy != dev->nBytesPerChunk) { + if (cache || nToCopy != dev->nDataBytesPerChunk) { if (dev->nShortOpCaches > 0) { /* If we can't find the data in the cache, then load it up. */ @@ -3856,7 +4684,7 @@ int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, __u32 offset, #ifdef CONFIG_YAFFS_WINCE yfsd_UnlockYAFFS(TRUE); #endif - memcpy(buffer, localBuffer, dev->nBytesPerChunk); + memcpy(buffer, localBuffer, dev->nDataBytesPerChunk); #ifdef CONFIG_YAFFS_WINCE yfsd_LockYAFFS(TRUE); @@ -3879,7 +4707,7 @@ int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, __u32 offset, return nDone; } -int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, __u32 offset, +int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset, int nBytes, int writeThrough) { @@ -3898,14 +4726,16 @@ int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, __u32 offset, dev = in->myDev; while (n > 0 && chunkWritten >= 0) { - chunk = offset / dev->nBytesPerChunk + 1; - start = offset % dev->nBytesPerChunk; + //chunk = offset / dev->nDataBytesPerChunk + 1; + //start = offset % dev->nDataBytesPerChunk; + yaffs_AddrToChunk(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->nBytesPerChunk) { + if ((start + n) < dev->nDataBytesPerChunk) { nToCopy = n; /* Now folks, to calculate how many bytes to write back.... @@ -3915,10 +4745,10 @@ int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, __u32 offset, nBytesRead = in->variant.fileVariant.fileSize - - ((chunk - 1) * dev->nBytesPerChunk); + ((chunk - 1) * dev->nDataBytesPerChunk); - if (nBytesRead > dev->nBytesPerChunk) { - nBytesRead = dev->nBytesPerChunk; + if (nBytesRead > dev->nDataBytesPerChunk) { + nBytesRead = dev->nDataBytesPerChunk; } nToWriteBack = @@ -3926,11 +4756,11 @@ int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, __u32 offset, (start + n)) ? nBytesRead : (start + n); } else { - nToCopy = dev->nBytesPerChunk - start; - nToWriteBack = dev->nBytesPerChunk; + nToCopy = dev->nDataBytesPerChunk - start; + nToWriteBack = dev->nDataBytesPerChunk; } - if (nToCopy != dev->nBytesPerChunk) { + if (nToCopy != dev->nDataBytesPerChunk) { /* An incomplete start or end chunk (or maybe both start and end chunk) */ if (dev->nShortOpCaches > 0) { yaffs_ChunkCache *cache; @@ -4028,20 +4858,20 @@ int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, __u32 offset, #ifdef CONFIG_YAFFS_WINCE yfsd_UnlockYAFFS(TRUE); #endif - memcpy(localBuffer, buffer, dev->nBytesPerChunk); + memcpy(localBuffer, buffer, dev->nDataBytesPerChunk); #ifdef CONFIG_YAFFS_WINCE yfsd_LockYAFFS(TRUE); #endif chunkWritten = yaffs_WriteChunkDataToObject(in, chunk, localBuffer, - dev->nBytesPerChunk, + 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->nBytesPerChunk, + dev->nDataBytesPerChunk, 0); #endif /* Since we've overwritten the cached data, we better invalidate it. */ @@ -4077,10 +4907,10 @@ static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize) yaffs_Device *dev = in->myDev; int oldFileSize = in->variant.fileVariant.fileSize; - int lastDel = 1 + (oldFileSize - 1) / dev->nBytesPerChunk; + int lastDel = 1 + (oldFileSize - 1) / dev->nDataBytesPerChunk; - int startDel = 1 + (newSize + dev->nBytesPerChunk - 1) / - dev->nBytesPerChunk; + int startDel = 1 + (newSize + dev->nDataBytesPerChunk - 1) / + dev->nDataBytesPerChunk; int i; int chunkId; @@ -4112,14 +4942,16 @@ static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize) } -int yaffs_ResizeFile(yaffs_Object * in, int newSize) +int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize) { int oldFileSize = in->variant.fileVariant.fileSize; - int sizeOfPartialChunk; + int newSizeOfPartialChunk; + int newFullChunks; + yaffs_Device *dev = in->myDev; - sizeOfPartialChunk = newSize % dev->nBytesPerChunk; + yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk); yaffs_FlushFilesChunkCache(in); yaffs_InvalidateWholeChunkCache(in); @@ -4138,19 +4970,20 @@ int yaffs_ResizeFile(yaffs_Object * in, int newSize) yaffs_PruneResizedChunks(in, newSize); - if (sizeOfPartialChunk != 0) { - int lastChunk = 1 + newSize / dev->nBytesPerChunk; + if (newSizeOfPartialChunk != 0) { + int lastChunk = 1 + newFullChunks; + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); /* Got to read and rewrite the last chunk with its new size and zero pad */ yaffs_ReadChunkDataFromObject(in, lastChunk, localBuffer); - memset(localBuffer + sizeOfPartialChunk, 0, - dev->nBytesPerChunk - sizeOfPartialChunk); + memset(localBuffer + newSizeOfPartialChunk, 0, + dev->nDataBytesPerChunk - newSizeOfPartialChunk); yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer, - sizeOfPartialChunk, 1); + newSizeOfPartialChunk, 1); yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); } @@ -4158,7 +4991,13 @@ int yaffs_ResizeFile(yaffs_Object * in, int newSize) in->variant.fileVariant.fileSize = newSize; yaffs_PruneFileStructure(dev, &in->variant.fileVariant); + } else { + /* newsSize > oldFileSize */ + in->variant.fileVariant.fileSize = newSize; } + + + /* Write a new object header. * show we've shrunk the file, if need be * Do this only if the file is not in the deleted directories. @@ -4169,7 +5008,7 @@ int yaffs_ResizeFile(yaffs_Object * in, int newSize) (newSize < oldFileSize) ? 1 : 0, 0); } - return newSize; + return YAFFS_OK; } loff_t yaffs_GetFileSize(yaffs_Object * obj) @@ -4222,7 +5061,7 @@ static int yaffs_DoGenericObjectDeletion(yaffs_Object * 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, NULL, 0, 0); + yaffs_ChangeObjectName(in, in->myDev->deletedDir,"deleted", 0, 0); } @@ -4260,7 +5099,7 @@ static int yaffs_UnlinkFile(yaffs_Object * in) if (immediateDeletion) { retVal = yaffs_ChangeObjectName(in, in->myDev->deletedDir, - NULL, 0, 0); + "deleted", 0, 0); T(YAFFS_TRACE_TRACING, (TSTR("yaffs: immediate deletion of file %d" TENDSTR), in->objectId)); @@ -4273,7 +5112,7 @@ static int yaffs_UnlinkFile(yaffs_Object * in) } else { retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir, - NULL, 0, 0); + "unlinked", 0, 0); } } @@ -4527,19 +5366,22 @@ static int yaffs_Scan(yaffs_Device * dev) int startIterator; int endIterator; int nBlocksToScan = 0; + int result; int chunk; int c; int deleted; yaffs_BlockState state; yaffs_Object *hardList = NULL; - yaffs_Object *hl; 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; @@ -4563,6 +5405,8 @@ static int yaffs_Scan(yaffs_Device * dev) if (dev->isYaffs2) { blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); + if(!blockIndex) + return YAFFS_FAIL; } /* Scan all the blocks to determine their state */ @@ -4644,7 +5488,7 @@ static int yaffs_Scan(yaffs_Device * dev) } /* For each block.... */ - for (blockIterator = startIterator; blockIterator <= endIterator; + for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; blockIterator++) { if (dev->isYaffs2) { @@ -4660,12 +5504,12 @@ static int yaffs_Scan(yaffs_Device * dev) deleted = 0; /* For each chunk in each block that needs scanning....*/ - for (c = 0; c < dev->nChunksPerBlock && + 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, + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, &tags); /* Let's have a good look at this chunk... */ @@ -4728,12 +5572,20 @@ static int yaffs_Scan(yaffs_Device * dev) /* PutChunkIntoFile checks for a clash (two data chunks with * the same chunkId). */ - yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, - 1); + + if(!in) + alloc_failed = 1; + + if(in){ + if(!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,1)) + alloc_failed = 1; + } + endpos = - (tags.chunkId - 1) * dev->nBytesPerChunk + + (tags.chunkId - 1) * dev->nDataBytesPerChunk + tags.byteCount; - if (in->variantType == YAFFS_OBJECT_TYPE_FILE + if (in && + in->variantType == YAFFS_OBJECT_TYPE_FILE && in->variant.fileVariant.scannedFileSize < endpos) { in->variant.fileVariant. @@ -4754,7 +5606,7 @@ static int yaffs_Scan(yaffs_Device * dev) yaffs_SetChunkBit(dev, blk, c); bi->pagesInUse++; - yaffs_ReadChunkWithTagsFromNAND(dev, chunk, + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, chunkData, NULL); @@ -4778,14 +5630,17 @@ static int yaffs_Scan(yaffs_Device * dev) objectId, oh->type); - if (oh->shadowsObject > 0) { + if(!in) + alloc_failed = 1; + + if (in && oh->shadowsObject > 0) { yaffs_HandleShadowedObject(dev, oh-> shadowsObject, 0); } - if (in->valid) { + if (in && in->valid) { /* We have already filled this one. We have a duplicate and need to resolve it. */ unsigned existingSerial = in->serial; @@ -4806,7 +5661,7 @@ static int yaffs_Scan(yaffs_Device * dev) } } - if (!in->valid && + 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 */ @@ -4831,7 +5686,7 @@ static int yaffs_Scan(yaffs_Device * dev) #endif in->chunkId = chunk; - } else if (!in->valid) { + } else if (in && !in->valid) { /* we need to load this info */ in->valid = 1; @@ -4940,9 +5795,10 @@ static int yaffs_Scan(yaffs_Device * dev) /* Do nothing */ break; case YAFFS_OBJECT_TYPE_SYMLINK: - in->variant.symLinkVariant. - alias = + in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; break; } @@ -5004,7 +5860,12 @@ static int yaffs_Scan(yaffs_Device * dev) yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + if(alloc_failed){ + return YAFFS_FAIL; + } + T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR))); + return YAFFS_OK; } @@ -5014,12 +5875,24 @@ static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) __u8 *chunkData; yaffs_ObjectHeader *oh; yaffs_Device *dev = in->myDev; - + yaffs_ExtendedTags tags; + int result; + int alloc_failed = 0; + + 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,NULL); + result = yaffs_ReadChunkWithTagsFromNAND(dev,in->chunkId,chunkData,&tags); oh = (yaffs_ObjectHeader *) chunkData; in->yst_mode = oh->yst_mode; @@ -5041,9 +5914,12 @@ static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) #endif yaffs_SetObjectName(in, oh->name); - if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK) + if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK){ in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; /* Not returned to caller */ + } yaffs_ReleaseTempBuffer(dev,chunkData, __LINE__); } @@ -5059,6 +5935,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) int nBlocksToScan = 0; int chunk; + int result; int c; int deleted; yaffs_BlockState state; @@ -5076,6 +5953,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) int isShrink; int foundChunksInBlock; int equivalentObjectId; + int alloc_failed = 0; yaffs_BlockIndex *blockIndex = NULL; @@ -5108,6 +5986,8 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) return YAFFS_FAIL; } + dev->blocksInCheckpoint = 0; + chunkData = yaffs_GetTempBuffer(dev, __LINE__); /* Scan all the blocks to determine their state */ @@ -5131,7 +6011,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) if(state == YAFFS_BLOCK_STATE_CHECKPOINT){ - /* todo .. fix free space ? */ + dev->blocksInCheckpoint++; } else if (state == YAFFS_BLOCK_STATE_DEAD) { T(YAFFS_TRACE_BAD_BLOCKS, @@ -5178,7 +6058,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) #ifndef CONFIG_YAFFS_USE_OWN_SORT { /* Use qsort now. */ - qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp); + yaffs_qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp); } #else { @@ -5209,7 +6089,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); /* For each block.... backwards */ - for (blockIterator = endIterator; blockIterator >= startIterator; + for (blockIterator = endIterator; !alloc_failed && blockIterator >= startIterator; blockIterator--) { /* Cooperative multitasking! This loop can run for so long that watchdog timers expire. */ @@ -5219,21 +6099,25 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) blk = blockIndex[blockIterator].block; bi = yaffs_GetBlockInfo(dev, blk); + + state = bi->blockState; deleted = 0; /* For each chunk in each block that needs scanning.... */ foundChunksInBlock = 0; - for (c = dev->nChunksPerBlock - 1; c >= 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, + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, &tags); /* Let's have a good look at this chunk... */ @@ -5257,44 +6141,46 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) state = YAFFS_BLOCK_STATE_EMPTY; dev->nErasedBlocks++; } else { - /* this is the block being allocated from */ - if (state == - YAFFS_BLOCK_STATE_NEEDS_SCANNING) { - T(YAFFS_TRACE_SCAN, - (TSTR - (" Allocating from %d %d" - TENDSTR), blk, c)); + 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)); + } + } - 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++; + } else if (tags.chunkId > 0) { /* chunkId > 0 so it is a data chunk... */ unsigned int endpos; __u32 chunkBase = - (tags.chunkId - 1) * dev->nBytesPerChunk; + (tags.chunkId - 1) * dev->nDataBytesPerChunk; foundChunksInBlock = 1; @@ -5306,19 +6192,27 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) tags. objectId, YAFFS_OBJECT_TYPE_FILE); - if (in->variantType == 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 */ - yaffs_PutChunkIntoFile(in, tags.chunkId, - chunk, -1); + if(!yaffs_PutChunkIntoFile(in, tags.chunkId, + chunk, -1)){ + alloc_failed = 1; + } /* File size is calculated by looking at the data chunks if we have not * seen an object header yet. Stop this practice once we find an object header. */ endpos = (tags.chunkId - - 1) * dev->nBytesPerChunk + + 1) * dev->nDataBytesPerChunk + tags.byteCount; if (!in->valid && /* have not got an object header yet */ @@ -5332,7 +6226,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) scannedFileSize; } - } else { + } else if(in) { /* This chunk has been invalidated by a resize, so delete */ yaffs_DeleteChunk(dev, chunk, 1, __LINE__); @@ -5370,7 +6264,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) * living with invalid data until needed. */ - yaffs_ReadChunkWithTagsFromNAND(dev, + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, chunkData, NULL); @@ -5620,16 +6514,21 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) /* Do nothing */ break; case YAFFS_OBJECT_TYPE_SYMLINK: - if(oh) + if(oh){ in->variant.symLinkVariant.alias = yaffs_CloneString(oh-> alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; + } break; } } + } - } + + } /* 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. */ @@ -5692,6 +6591,10 @@ static int yaffs_ScanBackwards(yaffs_Device * dev) } yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + + if(alloc_failed){ + return YAFFS_FAIL; + } T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR))); @@ -5799,7 +6702,7 @@ yaffs_Object *yaffs_FindObjectByName(yaffs_Object * directory, */ yaffs_GetObjectName(l, buffer, YAFFS_MAX_NAME_LENGTH); - if (yaffs_strcmp(name, buffer) == 0) { + if (yaffs_strncmp(name, buffer,YAFFS_MAX_NAME_LENGTH) == 0) { return l; } @@ -5855,6 +6758,7 @@ yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * 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); } return obj; @@ -5882,14 +6786,15 @@ int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize) } #endif else { + int result; __u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__); yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *) buffer; - memset(buffer, 0, obj->myDev->nBytesPerChunk); + memset(buffer, 0, obj->myDev->nDataBytesPerChunk); if (obj->chunkId >= 0) { - yaffs_ReadChunkWithTagsFromNAND(obj->myDev, + result = yaffs_ReadChunkWithTagsFromNAND(obj->myDev, obj->chunkId, buffer, NULL); } @@ -5914,7 +6819,7 @@ int yaffs_GetObjectFileLength(yaffs_Object * obj) return yaffs_strlen(obj->variant.symLinkVariant.alias); } else { /* Only a directory should drop through to here */ - return obj->myDev->nBytesPerChunk; + return obj->myDev->nDataBytesPerChunk; } } @@ -6091,7 +6996,7 @@ static int yaffs_CheckDevFunctions(const yaffs_Device * dev) } -static void yaffs_CreateInitialDirectories(yaffs_Device *dev) +static int yaffs_CreateInitialDirectories(yaffs_Device *dev) { /* Initialise the unlinked, deleted, root and lost and found directories */ @@ -6100,6 +7005,7 @@ static void yaffs_CreateInitialDirectories(yaffs_Device *dev) dev->unlinkedDir = yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); + dev->deletedDir = yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); @@ -6109,14 +7015,20 @@ static void yaffs_CreateInitialDirectories(yaffs_Device *dev) dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND, YAFFS_LOSTNFOUND_MODE | S_IFDIR); - yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); + + if(dev->lostNFoundDir && dev->rootDir && dev->unlinkedDir && dev->deletedDir){ + yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); + return YAFFS_OK; + } + + return YAFFS_FAIL; } int yaffs_GutsInitialise(yaffs_Device * dev) { + int init_failed = 0; unsigned x; int bits; - int extraBits; T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR))); @@ -6142,8 +7054,8 @@ int yaffs_GutsInitialise(yaffs_Device * dev) /* Check geometry parameters. */ - if ((dev->isYaffs2 && dev->nBytesPerChunk < 1024) || - (!dev->isYaffs2 && dev->nBytesPerChunk != 512) || + if ((dev->isYaffs2 && dev->nDataBytesPerChunk < 1024) || + (!dev->isYaffs2 && dev->nDataBytesPerChunk != 512) || dev->nChunksPerBlock < 2 || dev->nReservedBlocks < 2 || dev->internalStartBlock <= 0 || @@ -6153,7 +7065,7 @@ int yaffs_GutsInitialise(yaffs_Device * dev) T(YAFFS_TRACE_ALWAYS, (TSTR ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s " - TENDSTR), dev->nBytesPerChunk, dev->isYaffs2 ? "2" : "")); + TENDSTR), dev->nDataBytesPerChunk, dev->isYaffs2 ? "2" : "")); return YAFFS_FAIL; } @@ -6192,21 +7104,38 @@ int yaffs_GutsInitialise(yaffs_Device * dev) - /* OK now calculate a few things for the device + /* 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<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<crumbShift)-1; + dev->crumbsPerChunk = dev->nDataBytesPerChunk/(1 << dev->crumbShift); + dev->chunkShift = 0; + dev->chunkMask = 0; + } + + + /* * Calculate chunkGroupBits. * We need to find the next power of 2 > than internalEndBlock */ x = dev->nChunksPerBlock * (dev->internalEndBlock + 1); - - for (bits = extraBits = 0; x > 1; bits++) { - if (x & 1) - extraBits++; - x >>= 1; - } - - if (extraBits > 0) - bits++; + + bits = ShiftsGE(x); /* Set up tnode width if wide tnodes are enabled. */ if(!dev->wideTnodesDisabled){ @@ -6266,73 +7195,114 @@ int yaffs_GutsInitialise(yaffs_Device * dev) dev->nErasureFailures = 0; dev->nErasedBlocks = 0; dev->isDoingGC = 0; + dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */ /* Initialise temporary buffers and caches. */ - { - int i; - for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { - dev->tempBuffer[i].line = 0; /* not in use */ - dev->tempBuffer[i].buffer = - YMALLOC_DMA(dev->nBytesPerChunk); - } - } + if(!yaffs_InitialiseTempBuffers(dev)) + init_failed = 1; - if (dev->nShortOpCaches > 0) { + dev->srCache = NULL; + dev->gcCleanupList = NULL; + + + if (!init_failed && + dev->nShortOpCaches > 0) { int i; + __u8 *buf; + int srCacheBytes = dev->nShortOpCaches * sizeof(yaffs_ChunkCache); if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) { dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES; } - dev->srCache = - YMALLOC(dev->nShortOpCaches * sizeof(yaffs_ChunkCache)); - - for (i = 0; i < dev->nShortOpCaches; i++) { + buf = dev->srCache = YMALLOC(srCacheBytes); + + if(dev->srCache) + memset(dev->srCache,0,srCacheBytes); + + 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 = YMALLOC_DMA(dev->nBytesPerChunk); + dev->srCache[i].data = buf = YMALLOC_DMA(dev->nDataBytesPerChunk); } + if(!buf) + init_failed = 1; + dev->srLastUse = 0; } dev->cacheHits = 0; - dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); + if(!init_failed){ + dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); + if(!dev->gcCleanupList) + init_failed = 1; + } if (dev->isYaffs2) { dev->useHeaderFileSize = 1; } - - yaffs_InitialiseBlocks(dev); + if(!init_failed && !yaffs_InitialiseBlocks(dev)) + init_failed = 1; + yaffs_InitialiseTnodes(dev); yaffs_InitialiseObjects(dev); - yaffs_CreateInitialDirectories(dev); + if(!init_failed && !yaffs_CreateInitialDirectories(dev)) + init_failed = 1; - /* Now scan the flash. */ - if (dev->isYaffs2) { - if(yaffs_CheckpointRestore(dev)) { - T(YAFFS_TRACE_ALWAYS, - (TSTR("yaffs: restored from checkpoint" TENDSTR))); - } else { + if(!init_failed){ + /* Now scan the flash. */ + if (dev->isYaffs2) { + if(yaffs_CheckpointRestore(dev)) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: restored from checkpoint" TENDSTR))); + } else { - /* Clean up the mess caused by an aborted checkpoint load - * and scan backwards. - */ - yaffs_DeinitialiseBlocks(dev); - yaffs_DeinitialiseTnodes(dev); - yaffs_DeinitialiseObjects(dev); - yaffs_InitialiseBlocks(dev); - yaffs_InitialiseTnodes(dev); - yaffs_InitialiseObjects(dev); - yaffs_CreateInitialDirectories(dev); - - yaffs_ScanBackwards(dev); - } - }else - yaffs_Scan(dev); + /* Clean up the mess caused by an aborted checkpoint load + * and scan backwards. + */ + yaffs_DeinitialiseBlocks(dev); + yaffs_DeinitialiseTnodes(dev); + yaffs_DeinitialiseObjects(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; + + if(!init_failed && !yaffs_InitialiseBlocks(dev)) + init_failed = 1; + + yaffs_InitialiseTnodes(dev); + yaffs_InitialiseObjects(dev); + + if(!init_failed && !yaffs_CreateInitialDirectories(dev)) + init_failed = 1; + + if(!init_failed && !yaffs_ScanBackwards(dev)) + init_failed = 1; + } + }else + if(!yaffs_Scan(dev)) + init_failed = 1; + } + + if(init_failed){ + /* Clean up the mess */ + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: yaffs_GutsInitialise() aborted.\n" TENDSTR))); + + yaffs_Deinitialise(dev); + return YAFFS_FAIL; + } /* Zero out stats */ dev->nPageReads = 0; @@ -6344,6 +7314,8 @@ int yaffs_GutsInitialise(yaffs_Device * dev) dev->nRetiredBlocks = 0; yaffs_VerifyFreeChunks(dev); + yaffs_VerifyBlocks(dev); + T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR))); @@ -6359,13 +7331,17 @@ void yaffs_Deinitialise(yaffs_Device * dev) yaffs_DeinitialiseBlocks(dev); yaffs_DeinitialiseTnodes(dev); yaffs_DeinitialiseObjects(dev); - if (dev->nShortOpCaches > 0) { + if (dev->nShortOpCaches > 0 && + dev->srCache) { for (i = 0; i < dev->nShortOpCaches; i++) { - YFREE(dev->srCache[i].data); + if(dev->srCache[i].data) + YFREE(dev->srCache[i].data); + dev->srCache[i].data = NULL; } YFREE(dev->srCache); + dev->srCache = NULL; } YFREE(dev->gcCleanupList); @@ -6456,9 +7432,15 @@ static int yaffs_freeVerificationFailures; static void yaffs_VerifyFreeChunks(yaffs_Device * dev) { - int counted = yaffs_CountFreeChunks(dev); + int counted; + int difference; + + if(yaffs_SkipVerification(dev)) + return; + + counted = yaffs_CountFreeChunks(dev); - int difference = dev->nFreeChunks - counted; + difference = dev->nFreeChunks - counted; if (difference) { T(YAFFS_TRACE_ALWAYS,