X-Git-Url: http://www.aleph1.co.uk/gitweb/?a=blobdiff_plain;f=yaffs_guts.c;h=32b755972370aae9773519c0704e1d69df651dad;hb=572f34a1cc2b7bddbd8d66f64be95ba158703a2e;hp=abb01e2c1694ede4e0be49e82b7c9e564c560029;hpb=e7944bf67d156b9ecb1c8cdf9223dfb0e3154ba4;p=yaffs%2F.git diff --git a/yaffs_guts.c b/yaffs_guts.c index abb01e2..32b7559 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -14,12 +14,17 @@ */ //yaffs_guts.c +const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.12 2002-11-08 07:19:41 charles Exp $"; + #include "yportenv.h" #include "yaffsinterface.h" #include "yaffs_guts.h" +#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2 + + // External functions for ECC on data void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); @@ -58,25 +63,22 @@ static int yaffs_CountBits(__u8 x) -// Device info -//static yaffs_Device *yaffs_device; -//yaffs_Object *yaffs_rootDir; -//yaffs_Object *yaffs_lostNFound; - - - // Local prototypes static int yaffs_CheckObjectHashSanity(yaffs_Device *dev); static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr); -static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr); +static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr); 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 char *name); +static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force); static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId); static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj); static int yaffs_CheckStructures(void); +static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit); +static int yaffs_DoGenericObjectDeletion(yaffs_Object *in); + +static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo); // Robustification static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND); @@ -87,6 +89,7 @@ static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaf static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND); +static int yaffs_UnlinkWorker(yaffs_Object *obj); static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1); @@ -107,7 +110,22 @@ static int yaffs_CheckFileSanity(yaffs_Object *in); #define yaffs_CheckFileSanity(in) #endif -static int __inline__ yaffs_HashFunction(int n) +static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in); +static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId); + + +static __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk) +{ + if(blk < dev->startBlock || blk > dev->endBlock) + { + T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: block %d is not valid" TENDSTR),blk)); + YBUG(); + } + return &dev->blockInfo[blk - dev->startBlock]; +} + + +static __inline__ int yaffs_HashFunction(int n) { return (n % YAFFS_NOBJECT_BUCKETS); } @@ -126,12 +144,22 @@ yaffs_Object *yaffs_LostNFound(yaffs_Device *dev) static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare) { + if(chunkInNAND < dev->startBlock * YAFFS_CHUNKS_PER_BLOCK) + { + T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND)); + return YAFFS_FAIL; + } + dev->nPageWrites++; return dev->writeChunkToNAND(dev,chunkInNAND,data,spare); } -int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare,int doErrorCorrection) +int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, + __u8 *data, + yaffs_Spare *spare, + int doErrorCorrection) { int retVal; __u8 calcEcc[3]; @@ -140,6 +168,9 @@ int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 dev->nPageReads++; + + + if(!spare && data) { // If we don't have a real spare, then we use a local one. @@ -159,20 +190,24 @@ int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 if(eccResult1>0) { - T((TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND)); + T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND)); + dev->eccFixed++; } else if(eccResult1<0) { - T((TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND)); + T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND)); + dev->eccUnfixed++; } if(eccResult2>0) { - T((TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND)); + T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND)); + dev->eccFixed++; } else if(eccResult2<0) { - T((TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND)); + T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND)); + dev->eccUnfixed++; } if(eccResult1 || eccResult2) @@ -181,13 +216,13 @@ int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 yaffs_HandleReadDataError(dev,chunkInNAND); } } + return retVal; } static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND) { -#if 1 static int init = 0; static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK]; @@ -208,7 +243,6 @@ static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return YAFFS_FAIL; if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL; -#endif return YAFFS_OK; @@ -244,9 +278,14 @@ static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 { // First check this chunk is erased... +#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK writeOk = yaffs_CheckChunkErased(dev,chunk); - - if(writeOk) +#endif + if(!writeOk) + { + T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk)); + } + else { writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare); } @@ -260,14 +299,15 @@ static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 // NB We check a raw read without ECC correction applied yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0); +#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare)) - { + { // Didn't verify - T((TSTR("**>> yaffs write failed on chunk %d" TENDSTR), chunk)); - // yaffs_DeleteChunk(dev,chunk); + T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk)); writeOk = 0; - } + } +#endif } if(writeOk) @@ -287,7 +327,7 @@ static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 if(attempts > 1) { - T((TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts)); + T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts)); dev->nRetriedWrites+= (attempts - 1); } @@ -312,7 +352,7 @@ static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND) yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK, NULL , &spare); yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK + 1, NULL , &spare); - dev->blockInfo[blockInNAND].blockState = YAFFS_BLOCK_STATE_DEAD; + yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD; dev->nRetiredBlocks++; } @@ -327,12 +367,21 @@ static int yaffs_RewriteBufferedBlock(yaffs_Device *dev) // Set current write block to the new block dev->doingBufferedBlockRewrite = 0; + + return 1; } static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND) { - + int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK; + + // Mark the block for retirement + yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND)); + + + //TODO // Just do a garbage collection on the affected block then retire the block // NB recursion } @@ -352,6 +401,12 @@ static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaf static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND) { + int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK; + + // Mark the block for retirement + yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1; + // Delete the chunk + yaffs_DeleteChunk(dev,chunkInNAND); } @@ -360,6 +415,7 @@ static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND) static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1) { + if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 || s0->tagByte0 != s1->tagByte0 || s0->tagByte1 != s1->tagByte1 || @@ -403,16 +459,36 @@ static __u16 yaffs_CalcNameSum(const char *name) __u16 i = 1; __u8 *bname = (__u8 *)name; - - while (*bname) + if(bname) { - sum += (*bname) * i; - i++; - bname++; + while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH)) + { +#ifdef CONFIG_YAFFS_WINCE + sum += toupper(*bname) * i; +#else + sum += (*bname) * i; +#endif + i++; + bname++; + } } return sum; } +void yaffs_SetObjectName(yaffs_Object *obj, const char *name) +{ +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + if(name && strlen(name) <= YAFFS_SHORT_NAME_LENGTH) + { + strcpy(obj->shortName,name); + } + else + { + obj->shortName[0]='\0'; + } +#endif + obj->sum = yaffs_CalcNameSum(name); +} void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare) { @@ -433,7 +509,7 @@ void yaffs_CalcTagsECC(yaffs_Tags *tags) for(i = 0; i < 8; i++) { - for(j = 1; j &0x7f; j<<=1) + for(j = 1; j &0xff; j<<=1) { bit++; if(b[i] & j) @@ -448,7 +524,7 @@ void yaffs_CalcTagsECC(yaffs_Tags *tags) } -void yaffs_CheckECCOnTags(yaffs_Tags *tags) +int yaffs_CheckECCOnTags(yaffs_Tags *tags) { unsigned ecc = tags->ecc; @@ -456,9 +532,9 @@ void yaffs_CheckECCOnTags(yaffs_Tags *tags) ecc ^= tags->ecc; - if(ecc) + if(ecc && ecc <= 64) { - // Needs fixing + // TODO: Handle the failure better. Retire? unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes; ecc--; @@ -467,7 +543,17 @@ void yaffs_CheckECCOnTags(yaffs_Tags *tags) // Now recvalc the ecc yaffs_CalcTagsECC(tags); + + return 1; // recovered error + } + else if(ecc) + { + // Wierd ecc failure value + // TODO Need to do somethiong here + return -1; //unrecovered error } + + return 0; } @@ -503,7 +589,7 @@ static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes) if (!newTnodes) { - YALERT("Could not malloc tnodes"); + T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR))); return YAFFS_FAIL; } @@ -519,11 +605,13 @@ static int yaffs_CreateTnodes(yaffs_Device *dev,int 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) { - YALERT("Could not add tnodes to management list"); + T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR))); + } else { @@ -533,7 +621,7 @@ static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes) } - YINFO("Tnodes created"); + T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR))); return YAFFS_OK; @@ -597,6 +685,7 @@ static void yaffs_InitialiseTnodes(yaffs_Device*dev) } +#if 0 void yaffs_TnodeTest(yaffs_Device *dev) { @@ -624,6 +713,8 @@ void yaffs_TnodeTest(yaffs_Device *dev) } } +#endif + ////////////////// END OF TNODE MANIPULATION /////////////////////////// @@ -645,17 +736,17 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure // Check sane level and chunk Id if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL) { - char str[50]; - sprintf(str,"Bad level %d",level); - YALERT(str); +// char str[50]; +// sprintf(str,"Bad level %d",level); +// YALERT(str); return NULL; } if(chunkId > YAFFS_MAX_CHUNK_ID) { - char str[50]; - sprintf(str,"Bad chunkId %d",chunkId); - YALERT(str); +// char str[50]; +// sprintf(str,"Bad chunkId %d",chunkId); +// YALERT(str); return NULL; } @@ -702,37 +793,38 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStru yaffs_Tnode *tn; int requiredTallness; + int i; + int l; - __u32 i; - __u32 l; - + __u32 x; + //T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId)); // Check sane level and page Id if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) { - char str[50]; - sprintf(str,"Bad level %d",fStruct->topLevel); - YALERT(str); +// char str[50]; +// sprintf(str,"Bad level %d",fStruct->topLevel); +// YALERT(str); return NULL; } if(chunkId > YAFFS_MAX_CHUNK_ID) { - char str[50]; - sprintf(str,"Bad chunkId %d",chunkId); - YALERT(str); +// char str[50]; +// sprintf(str,"Bad chunkId %d",chunkId); +// YALERT(str); return NULL; } // First check we're tall enough (ie enough topLevel) - i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS); + x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS); requiredTallness = 0; - while(i) + while(x) { - i >>= YAFFS_TNODES_INTERNAL_BITS; + x >>= YAFFS_TNODES_INTERNAL_BITS; requiredTallness++; } @@ -755,7 +847,7 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStru } else { - YALERT("No more tnodes"); + T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR))); } } @@ -769,19 +861,19 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStru tn = fStruct->top; while (l > 0 && tn) { - i = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & + x = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & YAFFS_TNODES_INTERNAL_MASK; //T((TSTR(" [%d:%d]"),l,i)); - if(!tn->internal[i]) + if(!tn->internal[x]) { //T((TSTR(" added"))); - tn->internal[i] = yaffs_GetTnode(dev); + tn->internal[x] = yaffs_GetTnode(dev); } - tn = tn->internal[i]; + tn = tn->internal[x]; l--; } @@ -791,10 +883,11 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStru return tn; } -// DeleteWorker scans backwards through the tnode tree and delets all the +// DeleteWorker scans backwards through the tnode tree and deletes all the // chunks and tnodes in the file +// Returns 1 if the tree was deleted. Returns 0 if it stopped early due to hitting the limit and the delete is incomplete. -static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset) +static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit) { int i; int chunkInInode; @@ -802,6 +895,7 @@ static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, i yaffs_Tags tags; int found; int chunkDeleted; + int allDone = 1; if(tn) @@ -809,21 +903,32 @@ static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, i if(level > 0) { - for(i = YAFFS_NTNODES_INTERNAL -1; i >= 0; i--) + for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--) { if(tn->internal[i]) { - yaffs_DeleteWorker(in,tn->internal[i],level - 1, - (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ); - yaffs_FreeTnode(in->myDev,tn->internal[i]); - tn->internal[i] = NULL; + if(limit && (*limit) < 0) + { + allDone = 0; + } + else + { + allDone = yaffs_DeleteWorker(in,tn->internal[i],level - 1, + (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit); + } + if(allDone) + { + yaffs_FreeTnode(in->myDev,tn->internal[i]); + tn->internal[i] = NULL; + } } } + return (allDone) ? 1 : 0; } else if(level == 0) { - for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) + for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) //NB Don't apply the limit here, always delete a whole level0 { if(tn->level0[i]) { @@ -852,6 +957,11 @@ static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, i if(found) { yaffs_DeleteChunk(in->myDev,theChunk); + in->nDataChunks--; + if(limit) + { + *limit = *limit-1; + } } @@ -859,11 +969,15 @@ static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, i } } + return 1; + } } + return 1; + } @@ -966,6 +1080,7 @@ static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStr + /////////////////////// End of File Structure functions. ///////////////// // yaffs_CreateFreeObjects creates a bunch more objects and @@ -984,7 +1099,7 @@ static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects) if (!newObjects) { - YALERT("Could not allocate more objects"); + T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR))); return YAFFS_FAIL; } @@ -1004,7 +1119,7 @@ static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects) list = YMALLOC(sizeof(yaffs_ObjectList)); if(!list) { - YALERT("Could not add Objects to management list"); + T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR))); } else { @@ -1014,8 +1129,6 @@ static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects) } - YINFO("Objects created"); - return YAFFS_OK; } @@ -1041,6 +1154,7 @@ static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev) // Now sweeten it up... memset(tn,0,sizeof(yaffs_Object)); + tn->myDev = dev; tn->chunkId = -1; tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; INIT_LIST_HEAD(&(tn->hardLinks)); @@ -1069,6 +1183,8 @@ static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u3 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->st_mode = mode; obj->myDev = dev; obj->chunkId = 0; // Not a valid chunk. @@ -1198,7 +1314,7 @@ static int yaffs_CreateNewObjectNumber(yaffs_Device *dev) int found = 0; struct list_head *i; - int n = bucket; + __u32 n = (__u32)bucket; //yaffs_CheckObjectHashSanity(); @@ -1231,16 +1347,16 @@ void yaffs_HashObject(yaffs_Object *in) if(!list_empty(&in->hashLink)) { - YINFO("!!!"); + //YINFO("!!!"); } - + list_add(&in->hashLink,&dev->objectBucket[bucket].list); dev->objectBucket[bucket].count++; } -yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number) +yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number) { int bucket = yaffs_HashFunction(number); struct list_head *i; @@ -1279,11 +1395,16 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectTyp theObject->renameAllowed = 1; theObject->unlinkAllowed = 1; theObject->objectId = number; - theObject->myDev = dev; yaffs_HashObject(theObject); theObject->variantType = type; - theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME; +#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]; +#else + theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME; +#endif switch(type) { case YAFFS_OBJECT_TYPE_FILE: @@ -1379,14 +1500,21 @@ yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type, in->variantType = type; in->st_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[0]; + +#else + in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME; in->st_rdev = rdev; in->st_uid = uid; in->st_gid = gid; - in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME; - +#endif in->nDataChunks = 0; - in->sum = yaffs_CalcNameSum(name); + yaffs_SetObjectName(in,name); in->dirty = 1; yaffs_AddObjectToDirectory(parent,in); @@ -1411,7 +1539,8 @@ yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type, break; } - if(yaffs_UpdateObjectHeader(in,name) < 0) + if(yaffs_GetNumberOfFreeChunks(dev) <= 0 || + yaffs_UpdateObjectHeader(in,name,0) < 0) { // Could not create the object header, fail the creation yaffs_UnlinkWorker(in); @@ -1461,25 +1590,34 @@ yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *e } -static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName) +static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName,int force) { - //yaffs_Device *dev = obj->myDev; + int unlinkOp; if(newDir == NULL) { newDir = obj->parent; // use the old directory } + + unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE); - // Only proceed if the new name does not exist and - // if we're putting it into a directory. - if(!yaffs_FindObjectByName(newDir,newName) && - newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) + // 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. + // Otherwise only proceed if the new name does not exist and if we're putting it into a directory. + if( (unlinkOp|| + force || + !yaffs_FindObjectByName(newDir,newName)) && + newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) { - obj->sum = yaffs_CalcNameSum(newName); + yaffs_SetObjectName(obj,newName); obj->dirty = 1; + yaffs_AddObjectToDirectory(newDir,obj); - if(yaffs_UpdateObjectHeader(obj,newName) >= 0) + if(unlinkOp) obj->unlinked = 1; + + + if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0) { return YAFFS_OK; } @@ -1488,14 +1626,27 @@ static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const return YAFFS_FAIL; } + + int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName) { yaffs_Object *obj; + int force = 0; + +#ifdef CONFIG_YAFFS_WINCE + // Special case for 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 && _stricmp(oldName,newName) == 0) + { + force = 1; + } +#endif obj = yaffs_FindObjectByName(oldDir,oldName); if(obj && obj->renameAllowed) { - return yaffs_ChangeObjectName(obj,newDir,newName); + return yaffs_ChangeObjectName(obj,newDir,newName,force); } return YAFFS_FAIL; } @@ -1522,7 +1673,7 @@ static int yaffs_CheckObjectHashSanity(yaffs_Device *dev) if(countEm != dev->objectBucket[bucket].count) { - YALERT("Inode hash inconsistency"); + T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR))); ok = YAFFS_FAIL; } } @@ -1530,6 +1681,7 @@ static int yaffs_CheckObjectHashSanity(yaffs_Device *dev) return ok; } +#if 0 void yaffs_ObjectTest(yaffs_Device *dev) { yaffs_Object *in[1000]; @@ -1586,17 +1738,23 @@ void yaffs_ObjectTest(yaffs_Device *dev) } - +#endif /////////////////////////// Block Management and Page Allocation /////////////////// -static void yaffs_InitialiseBlocks(yaffs_Device *dev) +static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks) { - //Todo we're assuming the malloc will pass. - dev->blockInfo = YMALLOC(dev->nBlocks * sizeof(yaffs_BlockInfo)); - memset(dev->blockInfo,0,dev->nBlocks * sizeof(yaffs_BlockInfo)); dev->allocationBlock = -1; // force it to get a new one + //Todo we're assuming the malloc will pass. + dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo)); + if(dev->blockInfo) + { + memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo)); + return YAFFS_OK; + } + return YAFFS_FAIL; + } static void yaffs_DeinitialiseBlocks(yaffs_Device *dev) @@ -1615,20 +1773,29 @@ static int yaffs_FindDirtiestBlock(yaffs_Device *dev) int i; int dirtiest = -1; int pagesInUse = 100; // silly big number + yaffs_BlockInfo *bi; for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++) { b++; - if (b > dev->endBlock) + if ( b < dev->startBlock || b > dev->endBlock) { b = dev->startBlock; } + + if(b < dev->startBlock || b > dev->endBlock) + { + T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b)); + YBUG(); + } + + bi = yaffs_GetBlockInfo(dev,b); - if(dev->blockInfo[b].blockState == YAFFS_BLOCK_STATE_FULL && - (dev->blockInfo)[b].pagesInUse < pagesInUse) + if(bi->blockState == YAFFS_BLOCK_STATE_FULL && + bi->pagesInUse < pagesInUse) { dirtiest = b; - pagesInUse = (dev->blockInfo)[b].pagesInUse; + pagesInUse = bi->pagesInUse; } } @@ -1638,36 +1805,65 @@ static int yaffs_FindDirtiestBlock(yaffs_Device *dev) } -static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve) +static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo) +{ + 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. + bi->blockState = YAFFS_BLOCK_STATE_DIRTY; + + if(!bi->needsRetiring) + { + erasedOk = yaffs_EraseBlockInNAND(dev,blockNo); + if(!erasedOk) + { + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo)); + } + } + + if( erasedOk ) + { + bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + bi->pagesInUse = 0; + bi->pageBits = 0; + + T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo)); + } + else + { + yaffs_RetireBlock(dev,blockNo); + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo)); + } +} + + +static int yaffs_FindBlockForAllocation(yaffs_Device *dev) { int i; + yaffs_BlockInfo *bi; - if(useReserve && dev->nErasedBlocks < 1) + if(dev->nErasedBlocks < 1) { // Hoosterman we've got a problem. // Can't get space to gc + T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during gc" TENDSTR))); + return -1; } - else if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS) - { - // We are not in GC, so we hold some in reserve so we can get - // a gc done. - } // Find an empty block. for(i = dev->startBlock; i <= dev->endBlock; i++) { - - if(dev->blockInfo[i].blockState == YAFFS_BLOCK_STATE_EMPTY) + bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) { - dev->blockInfo[i].blockState = YAFFS_BLOCK_STATE_ALLOCATING; - dev->nErasedBlocks--; - if(dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS) - { - dev->garbageCollectionRequired = 1; - } - + bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING; + dev->nErasedBlocks--; return i; } } @@ -1676,51 +1872,34 @@ static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve) } -static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo) -{ - yaffs_BlockInfo *bi = &dev->blockInfo[blockNo]; - - // Mark as dirty. - // If the block is still healthy erase it and mark as clean. - // If the block has had a data failure, then retire it. - bi->blockState = YAFFS_BLOCK_STATE_DIRTY; - - if(!bi->needsRetiring && yaffs_EraseBlockInNAND(dev,blockNo)) - { - bi->blockState = YAFFS_BLOCK_STATE_EMPTY; - dev->nErasedBlocks++; - bi->pagesInUse = 0; - bi->pageBits = 0; - - T((TSTR("Erased block %d" TENDSTR),blockNo)); - } - else - { - yaffs_RetireBlock(dev,blockNo); - T((TSTR("**>> Block %d retired" TENDSTR),blockNo)); - } -} - static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve) { int retVal; + yaffs_BlockInfo *bi; if(dev->allocationBlock < 0) { // Get next block to allocate off - dev->allocationBlock = yaffs_FindBlockForAllocation(dev,useReserve); + dev->allocationBlock = yaffs_FindBlockForAllocation(dev); dev->allocationPage = 0; } + if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS) + { + // Not enough space to allocate unless we're allowed to use the reserve. + return -1; + } + // Next page please.... if(dev->allocationBlock >= 0) { + bi = yaffs_GetBlockInfo(dev,dev->allocationBlock); + retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) + dev->allocationPage; - dev->blockInfo[dev->allocationBlock].pagesInUse++; - dev->blockInfo[dev->allocationBlock].pageBits |= - (1 << (dev->allocationPage)); + bi->pagesInUse++; + bi->pageBits |= (1 << (dev->allocationPage)); dev->allocationPage++; @@ -1729,24 +1908,28 @@ static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve) // If the block is full set the state to full if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK) { - dev->blockInfo[dev->allocationBlock].blockState = YAFFS_BLOCK_STATE_FULL; + bi->blockState = YAFFS_BLOCK_STATE_FULL; dev->allocationBlock = -1; } -#ifdef YAFFS_PARANOID - if(yaffs_CheckChunkErased(dev,retVal) == YAFFS_FAIL) - { - T((TSTR("..................Trying to allocate non-erased page %d" TENDSTR),retVal)); - } -#endif + return retVal; } - T((TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); + T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); return -1; } +// To determine if we have enough space we just look at the +// number of erased blocks. +// The cache is allowed to use reserved blocks. + +int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev) +{ + return (dev->nErasedBlocks >= YAFFS_RESERVED_BLOCKS); +} + int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block) { @@ -1759,7 +1942,7 @@ int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block) yaffs_Tags tags; __u8 buffer[YAFFS_BYTES_PER_CHUNK]; - yaffs_BlockInfo *bi = &dev->blockInfo[block]; + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block); yaffs_Object *object; @@ -1780,41 +1963,57 @@ int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block) yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1); - yaffs_GetTagsFromSpare(&spare,&tags); - tags.serialNumber++; - yaffs_LoadTagsIntoSpare(&spare,&tags); + yaffs_GetTagsFromSpare(dev,&spare,&tags); -#if 0 - newChunk = yaffs_AllocatePage(dev,1); - if(newChunk < 0) + object = yaffs_FindObjectByNumber(dev,tags.objectId); + + if(object && object->deleted && tags.chunkId != 0) { - return YAFFS_FAIL; + // Data chunk in a deleted file, throw it away + // It's a deleted data chunk, + // No need to copy this, just forget about it and fix up the + // object. + + yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); + object->nDataChunks--; } - - yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare); - -#else - newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1); -#endif - if(newChunk < 0) + else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */) { - return YAFFS_FAIL; + // Deleted object header with no data chunks. + // Can be discarded + object->chunkId = 0; + //Todo some clean up + } + 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++; + yaffs_LoadTagsIntoSpare(&spare,&tags); - object = yaffs_FindObjectByNumber(dev,tags.objectId); + + newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1); + + if(newChunk < 0) + { + return YAFFS_FAIL; + } - // Ok, now fix up the Tnodes etc. + // Ok, now fix up the Tnodes etc. - if(tags.chunkId == 0) - { - // It's a header - object->chunkId = newChunk; - } - else - { - // It's a data chunk - yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0); - + if(tags.chunkId == 0) + { + // It's a header + object->chunkId = newChunk; + } + else + { + // It's a data chunk + yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0); + } } yaffs_DeleteChunk(dev,oldChunk); @@ -1825,12 +2024,87 @@ int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block) return YAFFS_OK; } -int yaffs_CheckGarbageCollection(yaffs_Device *dev) + +static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev) +{ + // todo find a file to delete + struct list_head *i; + yaffs_Object *l; + + + // To the free chunks add the chunks that are in the deleted unlinked files. + list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children) + { + l = list_entry(i, yaffs_Object,siblings); + if(l->deleted) + { + return l; + } + } + return NULL; +} + +static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev) +{ + // This does background deletion on unlinked files.. only deleted ones. + // If we don't have a file we're working on then find one + if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0) + { + dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev); + } + + // OK, we're working on a file... + if(dev->unlinkedDeletion) + { + yaffs_Object *obj = dev->unlinkedDeletion; + int delresult; + int limit; // Number of chunks to delete in a file. + // NB this can be exceeded, but not by much. + + limit = 5; +#if 0 + if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER)) + { + limit = 50; // Doing GC soon, so dig deeper + } + else + { + limit = 5; + } +#endif + + delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit); + + if(obj->nDataChunks == 0) + { + // Done all the deleting of data chunks. + // Now dump the header and clean up + yaffs_FreeTnode(dev,obj->variant.fileVariant.top); + yaffs_DoGenericObjectDeletion(obj); + dev->nDeletedFiles--; + dev->nUnlinkedFiles--; + dev->nBackgroundDeletions++; + dev->unlinkedDeletion = NULL; + } + } +} + + + +static int yaffs_CheckGarbageCollection(yaffs_Device *dev) { int block; + yaffs_DoUnlinkedFileDeletion(dev); + + if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER)) + { + dev->garbageCollectionRequired = 1; + } + if(dev->garbageCollectionRequired) { + dev->garbageCollections++; dev->garbageCollectionRequired = 0; if(dev->blockSelectedForGC >= 0) { @@ -1873,9 +2147,10 @@ static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr) sparePtr->tagByte7 = tu->asBytes[7]; } -static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr) +static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr) { yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr; + int result; tu->asBytes[0]= sparePtr->tagByte0; tu->asBytes[1]= sparePtr->tagByte1; @@ -1886,7 +2161,15 @@ static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr) tu->asBytes[6]= sparePtr->tagByte6; tu->asBytes[7]= sparePtr->tagByte7; - yaffs_CheckECCOnTags(tagsPtr); + result = yaffs_CheckECCOnTags(tagsPtr); + if(result> 0) + { + dev->tagsEccFixed++; + } + else if(result <0) + { + dev->tagsEccUnfixed++; + } } static void yaffs_SpareInitialise(yaffs_Spare *spare) @@ -1902,7 +2185,7 @@ static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_ if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare,1) == YAFFS_OK) { *chunkDeleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0; - yaffs_GetTagsFromSpare(&spare,tags); + yaffs_GetTagsFromSpare(dev,&spare,tags); return YAFFS_OK; } else @@ -2080,7 +2363,7 @@ int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags } -#if YAFFS_PARANOID +#ifdef YAFFS_PARANOID static int yaffs_CheckFileSanity(yaffs_Object *in) { @@ -2187,7 +2470,9 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkIn if(existingChunk <=0) { //Hoosterman - how did this happen? - // todo + + T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: existing chunk < 0 in scan" TENDSTR))); + } @@ -2248,6 +2533,7 @@ static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId) int block; int page; yaffs_Spare spare; + yaffs_BlockInfo *bi; if(chunkId <= 0) return; @@ -2260,20 +2546,21 @@ static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId) yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare); yaffs_HandleUpdateChunk(dev,chunkId,&spare); + bi = yaffs_GetBlockInfo(dev,block); // Pull out of the management area. // If the whole block became dirty, this will kick off an erasure. - if( dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_ALLOCATING || - dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL) + if( bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || + bi->blockState == YAFFS_BLOCK_STATE_FULL) { dev->nFreeChunks++; - dev->blockInfo[block].pageBits &= ~(1 << page); - dev->blockInfo[block].pagesInUse--; + bi->pageBits &= ~(1 << page); + bi->pagesInUse--; - if( dev->blockInfo[block].pagesInUse == 0 && - dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL) + if(bi->pagesInUse == 0 && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { yaffs_BlockBecameDirty(dev,block); } @@ -2317,36 +2604,6 @@ int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 * yaffs_CalcTagsECC(&newTags); - - #if 0 - // Create new chunk in NAND - newChunkId = yaffs_AllocatePage(dev,useReserve); - - - if(newChunkId >= 0) - { - - - yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags); - - yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId); - - - if(prevChunkId >= 0) - { - yaffs_DeleteChunk(dev,prevChunkId); - - } - - yaffs_CheckFileSanity(in); - - return newChunkId; - } - - - return -1; -#else - newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve); if(newChunkId >= 0) { @@ -2363,7 +2620,7 @@ int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 * } return newChunkId; -#endif + @@ -2373,7 +2630,7 @@ int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 * // UpdateObjectHeader updates the header on NAND for an object. // If name is not NULL, then that new name is used. // -int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name) +int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force) { yaffs_Device *dev = in->myDev; @@ -2387,8 +2644,9 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name) yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew; yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld; + - if(!in->fake) + if(!in->fake || force) { yaffs_CheckGarbageCollection(dev); @@ -2404,26 +2662,47 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name) // Header data oh->type = in->variantType; - + oh->st_mode = in->st_mode; + +#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->st_uid = in->st_uid; oh->st_gid = in->st_gid; oh->st_atime = in->st_atime; oh->st_mtime = in->st_mtime; oh->st_ctime = in->st_ctime; oh->st_rdev = in->st_rdev; - - oh->parentObjectId = in->parent->objectId; - oh->sum = in->sum; +#endif + if(in->parent) + { + oh->parentObjectId = in->parent->objectId; + } + else + { + oh->parentObjectId = 0; + } + + //oh->sum = in->sum; if(name && *name) { memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH); } - else + else if(prevChunkId) { memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1); } + else + { + memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); + } switch(in->variantType) { @@ -2458,16 +2737,12 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name) yaffs_CalcTagsECC(&newTags); - - -#if 0 + // Create new chunk in NAND - newChunkId = yaffs_AllocatePage(dev,1); + newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1); if(newChunkId >= 0) { - - yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags); in->chunkId = newChunkId; @@ -2477,35 +2752,274 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name) } in->dirty = 0; - return newChunkId; } - - return -1; -#else - // Create new chunk in NAND - newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1); - - if(newChunkId >= 0) - { - in->chunkId = newChunkId; + return newChunkId; + + } + return 0; +} + + +/////////////////////// Short Operations Cache //////////////////////////////// +// In many siturations 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. +// There are a limited number (~10) of cache chunks per device so that we don't +// need a very intelligent search. + + +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + + +static void yaffs_FlushFilesChunkCache(yaffs_Object *obj) +{ + yaffs_Device *dev = obj->myDev; + int lowest; + int i; + yaffs_ChunkCache *cache; + int chunkWritten; + int nBytes; + + do{ + cache = NULL; - if(prevChunkId >= 0) + // Find the dirty cache for this object with the lowest chunk id. + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(dev->srCache[i].object == obj && + dev->srCache[i].dirty) { - yaffs_DeleteChunk(dev,prevChunkId); + if(!cache || dev->srCache[i].chunkId < lowest) + { + cache = &dev->srCache[i]; + lowest = cache->chunkId; + } } + } - in->dirty = 0; + if(cache) + { + //Write it out + + nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK); + + if(nBytes > YAFFS_BYTES_PER_CHUNK) + { + nBytes= YAFFS_BYTES_PER_CHUNK; + } + + chunkWritten = yaffs_WriteChunkDataToObject(cache->object, + cache->chunkId, + cache->data, + nBytes,1); + + cache->dirty = 0; } - return newChunkId; + } while(cache && chunkWritten > 0); + + if(cache) + { + //Hoosterman, disk full while writing cache out. + T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during caceh write" TENDSTR))); -#endif - } - return 0; + } + +} + + +// Grab us a 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 yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev) +{ + int i; + int usage; + int theOne; + + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(!dev->srCache[i].object) + { + //T(("Grabbing empty %d\n",i)); + + return &dev->srCache[i]; + } + } + + theOne = -1; + usage = 0; // just to stop the compiler grizzling + + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(!dev->srCache[i].dirty && + ((dev->srCache[i].lastUse < usage && theOne >= 0)|| + theOne < 0)) + { + usage = dev->srCache[i].lastUse; + theOne = i; + } + } + + //T(("Grabbing non-empty %d\n",theOne)); + return theOne >= 0 ? &dev->srCache[theOne] : NULL; + +} + + +static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev) +{ + yaffs_ChunkCache *cache; + yaffs_Object *theObj; + int usage; + int i; + + // Try find a non-dirty one... + + cache = yaffs_GrabChunkCacheWorker(dev); + + 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. + + theObj = dev->srCache[0].object; + usage = dev->srCache[0].lastUse; + + for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if( dev->srCache[i].object && + dev->srCache[i].lastUse < usage) + { + usage = dev->srCache[i].lastUse; + theObj = dev->srCache[i].object; + } + } + + yaffs_FlushFilesChunkCache(theObj); + + // Try again + cache = yaffs_GrabChunkCacheWorker(dev); + } + + return cache; + +} + + +// Find a cached chunk +static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId) +{ + yaffs_Device *dev = obj->myDev; + int i; + + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(dev->srCache[i].object == obj && + dev->srCache[i].chunkId == chunkId) + { + dev->cacheHits++; + + return &dev->srCache[i]; + } + } + + return NULL; +} + +// Mark the chunk for the least recently used algorithym +static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite) +{ + if( dev->srLastUse < 0 || + dev->srLastUse > 100000000) + { + // Reset the cache usages + int i; + for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++) + { + dev->srCache[i].lastUse = 0; + } + dev->srLastUse = 0; + } + + dev->srLastUse++; + + cache->lastUse = dev->srLastUse; + + if(isAWrite) + { + cache->dirty = 1; + } +} + +// 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) +{ + yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId); + + if(cache) + { + cache->object = NULL; + } +} + + +// 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; + + // Now invalidate it. + for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + if(dev->srCache[i].object == in) + { + dev->srCache[i].object = NULL; + } + } } +#else + +static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev) +{ + return NULL; +} + + +static yaffs_ChunkCache *yaffs_FindChunkCache(yaffs_Device *dev, int objectId, int chunkId) +{ + return NULL; +} + +static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache) +{ +} + +static void yaffs_InvalidateChunkCache(yaffs_Object *obj, int chunkId) +{ +} + +static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in) +{ +} + + + +#endif + + ///////////////////////// File read/write /////////////////////////////// // Read and write have very similar structures. @@ -2519,9 +3033,6 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name) int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes) { -// yaffs_Device *dev = in->myDev; - - __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; int chunk; int start; @@ -2548,15 +3059,42 @@ int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nB if(nToCopy != YAFFS_BYTES_PER_CHUNK) { // An incomplete start or end chunk (or maybe both start and end chunk) +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + yaffs_ChunkCache *cache; + // If we can't find the data in the cache, then load it up. + cache = yaffs_FindChunkCache(in,chunk); + if(!cache) + { + cache = yaffs_GrabChunkCache(in->myDev); + cache->object = in; + cache->chunkId = chunk; + cache->dirty = 0; + yaffs_ReadChunkDataFromObject(in,chunk,cache->data); + } + + yaffs_UseChunkCache(in->myDev,cache,0); + + memcpy(buffer,&cache->data[start],nToCopy); +#else + __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; // Read into the local buffer then copy... - yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); memcpy(buffer,&localBuffer[start],nToCopy); +#endif } else { +#ifdef CONFIG_YAFFS_WINCE + __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; + + // 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); + memcpy(buffer,localBuffer,YAFFS_BYTES_PER_CHUNK); +#else // A full chunk. Read directly into the supplied buffer. yaffs_ReadChunkDataFromObject(in,chunk,buffer); +#endif } n -= nToCopy; @@ -2570,9 +3108,9 @@ int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nB } + int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes) { - __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; int chunk; int start; @@ -2580,8 +3118,11 @@ int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, in int n = nBytes; int nDone = 0; int nToWriteBack; - int endOfWrite = offset+nBytes; + int startOfWrite = offset; int chunkWritten = 0; + int nBytesRead; + + while(n > 0 && chunkWritten >= 0) { @@ -2591,10 +3132,24 @@ int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, in // OK now check for the curveball where the start and end are in // the same chunk. + if( (start + n) < YAFFS_BYTES_PER_CHUNK) { nToCopy = n; - nToWriteBack = (start + 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. + + nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * YAFFS_BYTES_PER_CHUNK); + + if(nBytesRead > YAFFS_BYTES_PER_CHUNK) + { + nBytesRead = YAFFS_BYTES_PER_CHUNK; + } + + nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n); + } else { @@ -2605,6 +3160,32 @@ int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, in if(nToCopy != YAFFS_BYTES_PER_CHUNK) { // An incomplete start or end chunk (or maybe both start and end chunk) +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + yaffs_ChunkCache *cache; + // If we can't find the data in the cache, then load it up. + cache = yaffs_FindChunkCache(in,chunk); + if(!cache && yaffs_CheckSpaceForChunkCache(in->myDev)) + { + cache = yaffs_GrabChunkCache(in->myDev); + cache->object = in; + cache->chunkId = chunk; + cache->dirty = 0; + yaffs_ReadChunkDataFromObject(in,chunk,cache->data); + } + + if(cache) + { + yaffs_UseChunkCache(in->myDev,cache,1); + memcpy(&cache->data[start],buffer,nToCopy); + } + else + { + chunkWritten = -1; // fail the write + } +#else + __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; + + // 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. yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); @@ -2613,13 +3194,25 @@ int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, in chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0); - //T(("Write with readback to chunk %d %d\n",chunk,chunkWritten)); + //T(("Write with readback to chunk %d %d start %d copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack)); +#endif } else { + +#ifdef CONFIG_YAFFS_WINCE + __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; + // Under WinCE can't do direct transfer. Need to use a local buffer. + // This is because we otherwise screw up WinCE's memory mapper + memcpy(localBuffer,buffer,YAFFS_BYTES_PER_CHUNK); + chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,YAFFS_BYTES_PER_CHUNK,0); +#else // A full chunk. Write directly from the supplied buffer. chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0); +#endif + // Since we've overwritten the cached data, we better invalidate it. + yaffs_InvalidateChunkCache(in,chunk); //T(("Write to chunk %d %d\n",chunk,chunkWritten)); } @@ -2635,13 +3228,12 @@ int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, in // Update file object - if(endOfWrite > in->variant.fileVariant.fileSize) + if((startOfWrite + nDone) > in->variant.fileVariant.fileSize) { - in->variant.fileVariant.fileSize = endOfWrite; + in->variant.fileVariant.fileSize = (startOfWrite + nDone); } in->dirty = 1; - /*in->st_mtime = CURRENT_TIME; only update in flush*/ return nDone; } @@ -2657,6 +3249,9 @@ int yaffs_ResizeFile(yaffs_Object *in, int newSize) yaffs_Device *dev = in->myDev; __u8 localBuffer[YAFFS_BYTES_PER_CHUNK]; + + yaffs_FlushFilesChunkCache(in); + yaffs_InvalidateWholeChunkCache(in); if(in->variantType != YAFFS_OBJECT_TYPE_FILE) { @@ -2743,10 +3338,16 @@ int yaffs_FlushFile(yaffs_Object *in) if(in->dirty) { //T(("flushing object header\n")); + + yaffs_FlushFilesChunkCache(in); +#ifdef CONFIG_YAFFS_WINCE + yfsd_WinFileTimeNow(in->win_mtime); +#else in->st_mtime = CURRENT_TIME; +#endif - retVal = yaffs_UpdateObjectHeader(in,NULL); + retVal = yaffs_UpdateObjectHeader(in,NULL,0); } else { @@ -2760,8 +3361,19 @@ int yaffs_FlushFile(yaffs_Object *in) static int yaffs_DoGenericObjectDeletion(yaffs_Object *in) { + + // First off, invalidate the file's data in the cache, without flushing. + yaffs_InvalidateWholeChunkCache(in); + yaffs_RemoveObjectFromDirectory(in); yaffs_DeleteChunk(in->myDev,in->chunkId); +#ifdef __KERNEL__ + if(in->myInode) + { + in->myInode->u.generic_ip = NULL; + in->myInode = 0; + } +#endif yaffs_FreeObject(in); return YAFFS_OK; @@ -2770,19 +3382,71 @@ static int yaffs_DoGenericObjectDeletion(yaffs_Object *in) // yaffs_DeleteFile 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_DeleteFile(yaffs_Object *in) +static int yaffs_UnlinkFile(yaffs_Object *in) { + +#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION // Delete the file data & tnodes -#if 0 - yaffs_ResizeFile(in,0); -#else - yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0); -#endif + yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL); + yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top); return yaffs_DoGenericObjectDeletion(in); +#else + int retVal; + int immediateDeletion=0; + retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0); + if(retVal == YAFFS_OK) + { + //in->unlinked = 1; + //in->myDev->nUnlinkedFiles++; + //in->renameAllowed = 0; +#ifdef __KERNEL__ + if(in->myInode) + { + immediateDeletion = 1; + + } +#endif +#ifdef CONFIG_YAFFS_WINCE + if(in->inUse <= 0) + { + immediateDeletion = 1; + + } +#endif + + if(immediateDeletion) + { + T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId)); + in->deleted=1; + in->myDev->nDeletedFiles++; + } + + } + return retVal; + + +#endif +} + +int yaffs_DeleteFile(yaffs_Object *in) +{ + int retVal = YAFFS_OK; + if(!in->unlinked) + { + retVal = yaffs_UnlinkFile(in); + } + if(retVal == YAFFS_OK && + in->unlinked && + !in->deleted) + { + in->deleted = 1; + in->myDev->nDeletedFiles++; + } + return in->deleted ? YAFFS_OK : YAFFS_FAIL; } static int yaffs_DeleteDirectory(yaffs_Object *in) @@ -2823,87 +3487,6 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj) } else if(!list_empty(&obj->hardLinks)) { -#if 0 - // Curve ball: We're unlinking an object that has a hardlink. - // Therefore we can't really delete the object. - // Instead, we do the following: - // - Select a hardlink. - // - Re-type a hardlink as the equivalent object and populate the fields, including the - // objectId. Updating the object id is important so that all the hardlinks do not need - // to be rewritten. - // - Update the equivalet object pointers. - // - Delete all object. - - yaffs_Object *hl; - struct list_head *i; - - - yaffs_RemoveObjectFromDirectory(obj); - - - - hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks); - - hl->dirty = 1; - hl->st_mode = obj->st_mode; - hl->st_uid = obj->st_uid; - hl->st_gid = obj->st_gid; - hl->st_atime = obj->st_atime; - hl->st_mtime = obj->st_mtime; - hl->st_ctime = obj->st_ctime; - hl->st_rdev = obj->st_rdev; - - hl->variantType = obj->variantType; - - switch(hl->variantType) - { - case YAFFS_OBJECT_TYPE_FILE: - case YAFFS_OBJECT_TYPE_SYMLINK: - case YAFFS_OBJECT_TYPE_SPECIAL: - // These types are OK to just copy across. - hl->variant = obj->variant; - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - // Fix the list up - list_add(&hl->variant.directoryVariant.children, - &obj->variant.directoryVariant.children); - list_del(&obj->variant.directoryVariant.children); - - // Now change all the directory children to point to the new parent. - list_for_each(i,&hl->variant.directoryVariant.children) - { - list_entry(i,yaffs_Object,siblings)->parent = hl; - } - break; - - case YAFFS_OBJECT_TYPE_HARDLINK: - case YAFFS_OBJECT_TYPE_UNKNOWN: - // Should not be either of these types. - } - - // Now fix up the hardlink chain - list_del(&obj->hardLinks); - - list_for_each(i,&hl->hardLinks) - { - list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl; - list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId; - } - - // Now fix up the hash links. - yaffs_UnhashObject(hl); - hl->objectId = obj->objectId; - yaffs_HashObject(hl); - - // Update the hardlink which has become an object - yaffs_UpdateObjectHeader(hl,NULL); - - // Finally throw away the deleted object - yaffs_DeleteChunk(obj->myDev,obj->chunkId); - yaffs_FreeObject(obj); - - return YAFFS_OK; -#else // Curve ball: We're unlinking an object that has a hardlink. // // This problem arises because we are not strictly following @@ -2929,16 +3512,13 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj) yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1); - retVal = yaffs_ChangeObjectName(obj, hl->parent, name); + retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0); if(retVal == YAFFS_OK) { retVal = yaffs_DoGenericObjectDeletion(hl); } return retVal; - -#endif - } else @@ -2946,7 +3526,7 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj) switch(obj->variantType) { case YAFFS_OBJECT_TYPE_FILE: - return yaffs_DeleteFile(obj); + return yaffs_UnlinkFile(obj); break; case YAFFS_OBJECT_TYPE_DIRECTORY: return yaffs_DeleteDirectory(obj); @@ -3032,7 +3612,7 @@ static int yaffs_Scan(yaffs_Device *dev) yaffs_BlockState state; yaffs_Object *hardList = NULL; yaffs_Object *hl; - + yaffs_BlockInfo *bi; yaffs_ObjectHeader *oh; @@ -3047,15 +3627,16 @@ static int yaffs_Scan(yaffs_Device *dev) for(blk = dev->startBlock; blk <= dev->endBlock; blk++) { deleted = 0; - dev->blockInfo[blk].pageBits = 0; - dev->blockInfo[blk].pagesInUse = 0; + bi = yaffs_GetBlockInfo(dev,blk); + bi->pageBits = 0; + bi->pagesInUse = 0; state = YAFFS_BLOCK_STATE_SCANNING; if(yaffs_IsBlockBad(dev,blk)) { state = YAFFS_BLOCK_STATE_DEAD; - T((TSTR("block %d is bad" TENDSTR),blk)); + T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk)); } // Read each chunk in the block. @@ -3070,7 +3651,7 @@ static int yaffs_Scan(yaffs_Device *dev) // This block looks ok, now what's in this chunk? - yaffs_GetTagsFromSpare(&spare,&tags); + yaffs_GetTagsFromSpare(dev,&spare,&tags); if(yaffs_CountBits(spare.pageStatus) < 6) { @@ -3094,7 +3675,7 @@ static int yaffs_Scan(yaffs_Device *dev) else { // this is the block being allocated from - T((TSTR(" Allocating from %d %d" TENDSTR),blk,c)); + T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c)); state = YAFFS_BLOCK_STATE_ALLOCATING; dev->allocationBlock = blk; dev->allocationPage = c; @@ -3106,8 +3687,8 @@ static int yaffs_Scan(yaffs_Device *dev) { int endpos; // A data chunk. - dev->blockInfo[blk].pageBits |= (1 << c); - dev->blockInfo[blk].pagesInUse++; + bi->pageBits |= (1 << c); + bi->pagesInUse++; in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE); // PutChunkIntoFIle checks for a clash (two data chunks with @@ -3117,6 +3698,11 @@ static int yaffs_Scan(yaffs_Device *dev) if(in->variant.fileVariant.scannedFileSize variant.fileVariant.scannedFileSize = endpos; +#ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE + in->variant.fileVariant.fileSize = + in->variant.fileVariant.scannedFileSize; +#endif + } //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); } @@ -3124,8 +3710,8 @@ static int yaffs_Scan(yaffs_Device *dev) { // chunkId == 0, so it is an ObjectHeader. // Thus, we read in the object header and make the object - dev->blockInfo[blk].pageBits |= (1 << c); - dev->blockInfo[blk].pagesInUse++; + bi->pageBits |= (1 << c); + bi->pagesInUse++; yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1); @@ -3153,7 +3739,34 @@ static int yaffs_Scan(yaffs_Device *dev) } } - if(!in->valid) + 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; + in->variantType = oh->type; + + in->st_mode = oh->st_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->st_uid = oh->st_uid; + in->st_gid = oh->st_gid; + in->st_atime = oh->st_atime; + in->st_mtime = oh->st_mtime; + in->st_ctime = oh->st_ctime; + in->st_rdev = oh->st_rdev; +#endif + in->chunkId = chunk; + + } + else if(!in->valid) { // we need to load this info @@ -3161,15 +3774,24 @@ static int yaffs_Scan(yaffs_Device *dev) in->variantType = oh->type; in->st_mode = oh->st_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->st_uid = oh->st_uid; in->st_gid = oh->st_gid; in->st_atime = oh->st_atime; in->st_mtime = oh->st_mtime; in->st_ctime = oh->st_ctime; in->st_rdev = oh->st_rdev; +#endif in->chunkId = chunk; - in->sum = oh->sum; + yaffs_SetObjectName(in,oh->name); in->dirty = 0; // directory stuff... @@ -3187,9 +3809,16 @@ static int yaffs_Scan(yaffs_Device *dev) // Hoosterman, another problem.... // We're trying to use a non-directory as a directory // Todo ... handle + T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: attempting to use non-directory as a directory in scan" TENDSTR))); + } - yaffs_AddObjectToDirectory(parent,in); + yaffs_AddObjectToDirectory(parent,in); + if(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 @@ -3202,7 +3831,9 @@ static int yaffs_Scan(yaffs_Device *dev) case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem break; case YAFFS_OBJECT_TYPE_FILE: +#ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE in->variant.fileVariant.fileSize = oh->fileSize; +#endif break; case YAFFS_OBJECT_TYPE_HARDLINK: in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId; @@ -3228,11 +3859,11 @@ static int yaffs_Scan(yaffs_Device *dev) state = YAFFS_BLOCK_STATE_FULL; } - dev->blockInfo[blk].blockState = state; + bi->blockState = state; // Now let's see if it was dirty - if( dev->blockInfo[blk].pagesInUse == 0 && - dev->blockInfo[blk].blockState == YAFFS_BLOCK_STATE_FULL) + if( bi->pagesInUse == 0 && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { yaffs_BlockBecameDirty(dev,blk); } @@ -3292,6 +3923,13 @@ static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *ob // Now add it list_add(&obj->siblings,&directory->variant.directoryVariant.children); obj->parent = directory; + + if(directory == obj->myDev->unlinkedDir) + { + obj->unlinked = 1; + obj->myDev->nUnlinkedFiles++; + obj->renameAllowed = 0; + } } static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj) @@ -3390,6 +4028,12 @@ int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize) strncpy(name,locName,buffSize - 1); } +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + else if(obj->shortName[0]) + { + strcpy(name,obj->shortName); + } +#endif else { __u8 buffer[YAFFS_BYTES_PER_CHUNK]; @@ -3430,12 +4074,16 @@ int yaffs_GetObjectFileLength(yaffs_Object *obj) int yaffs_GetObjectLinkCount(yaffs_Object *obj) { - int count = 1; // the object itself + int count = 0; struct list_head *i; + if(!obj->unlinked) + { + count++; // the object itself + } list_for_each(i,&obj->hardLinks) { - count++; + count++; // add the hard links; } return count; @@ -3481,6 +4129,7 @@ char *yaffs_GetSymlinkAlias(yaffs_Object *obj) } } +#ifndef CONFIG_YAFFS_WINCE int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr) { @@ -3496,7 +4145,7 @@ int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr) if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size); - yaffs_UpdateObjectHeader(obj,NULL); + yaffs_UpdateObjectHeader(obj,NULL,1); return YAFFS_OK; @@ -3521,7 +4170,7 @@ int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr) } - +#endif int yaffs_DumpObject(yaffs_Object *obj) { @@ -3538,7 +4187,7 @@ int yaffs_DumpObject(yaffs_Object *obj) yaffs_GetObjectName(obj,name,256); - YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n", + 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))); @@ -3571,67 +4220,106 @@ int yaffs_DumpObject(yaffs_Object *obj) int yaffs_GutsInitialise(yaffs_Device *dev) { - unsigned nChunks,x; + unsigned x; int bits; + int extraBits; + int nBlocks; if(!yaffs_CheckStructures()) { + T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR))); return YAFFS_FAIL; } + + if(dev->startBlock <= 0 || + (dev->endBlock - dev->startBlock) < 10) + { + T(YAFFS_TRACE_ALWAYS,(TSTR("startBlock %d or endBlock %d invalid\n" TENDSTR), + dev->startBlock, dev->endBlock)); + return YAFFS_FAIL; + } + + nBlocks = dev->endBlock - dev->startBlock + 1; + // OK now calculate a few things for the device // Calculate chunkGroupBits. - // If there are 64k or less chunks then this is 1 - // Else it is log2(nChunks) - 16 - // - x = nChunks = YAFFS_CHUNKS_PER_BLOCK * dev->nBlocks; + // We need to find the next power of 2 > than endBlock + + x = YAFFS_CHUNKS_PER_BLOCK * (dev->endBlock+1); - for(bits = 0, x = nChunks; (x & 1) == 0; bits++) + for(bits = extraBits = 0; x > 1; bits++) { + if(x & 1) extraBits++; x >>= 1; } + + if(extraBits > 0) bits++; - if( x != 1) - { - // Not a power of 2 - YPRINTF(("nBlocks should be a power of 2 but is %u\n", - dev->nBlocks)); - return YAFFS_FAIL; - } + // Level0 Tnodes are 16 bits, so if the bitwidth of the + // chunk range we're using is greater than 16 we need + // to figure out chunk shift and chunkGroupSize if(bits <= 16) { dev->chunkGroupBits = 0; - dev->chunkGroupSize = 1; } else { dev->chunkGroupBits = bits - 16; - dev->chunkGroupSize = nChunks>> 16; } + dev->chunkGroupSize = 1 << dev->chunkGroupBits; + // More device initialisation dev->garbageCollectionRequired = 0; + dev->garbageCollections = 0; dev->currentDirtyChecker = 0; dev->bufferedBlock = -1; dev->doingBufferedBlockRewrite = 0; dev->blockSelectedForGC = -1; + dev->nDeletedFiles = 0; + dev->nBackgroundDeletions=0; + dev->nUnlinkedFiles = 0; + dev->eccFixed=0; + dev->eccUnfixed=0; + dev->tagsEccFixed=0; + dev->tagsEccUnfixed=0; - yaffs_InitialiseBlocks(dev); + yaffs_InitialiseBlocks(dev,nBlocks); yaffs_InitialiseTnodes(dev); yaffs_InitialiseObjects(dev); +#ifdef CONFIG_YAFFS_SHORT_OP_CACHE + { + int i; + for(i=0; i < YAFFS_N_CACHE_CHUNKS; i++) + { + dev->srCache[i].object = NULL; + dev->srCache[i].lastUse = 0; + dev->srCache[i].dirty = 0; + } + dev->srLastUse = 0; + } +#endif + + dev->cacheHits = 0; - // Initialise the root and lost and found directories - dev->lostNFoundDir = dev->rootDir = NULL; + + // Initialise the unlinked, root and lost and found directories + dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = NULL; + + dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR); + dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR); - dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_ROOT_MODE | S_IFDIR); + dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR); yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir); + // Now scan the flash. yaffs_Scan(dev); @@ -3660,6 +4348,22 @@ void yaffs_Deinitialise(yaffs_Device *dev) int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev) { int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS); + + struct list_head *i; + yaffs_Object *l; + + + // To the free chunks add the chunks that are in the deleted unlinked files. + list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children) + { + l = list_entry(i, yaffs_Object,siblings); + if(l->deleted) + { + nFree++; + nFree += l->nDataChunks; + } + } + return (nFree < 0) ? 0 : nFree; @@ -3670,7 +4374,8 @@ int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev) #define yaffs_CheckStruct(structure,syze, name) \ if(sizeof(structure) != syze) \ - { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \ + { \ + T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),name,syze,sizeof(structure))); \ return YAFFS_FAIL; \ } @@ -3687,21 +4392,20 @@ static int yaffs_CheckStructures(void) return YAFFS_OK; } +#if 0 void yaffs_GutsTest(yaffs_Device *dev) { if(yaffs_CheckStructures() != YAFFS_OK) { - YPRINTF(("One or more structures malformed-- aborting\n")); + T(YAFFS_TRACE_ALWAYS,(TSTR("One or more structures malformed-- aborting\n" TENDSTR))); return; } - else - { - YPRINTF(("Structures OK\n")); - } yaffs_TnodeTest(dev); yaffs_ObjectTest(dev); } +#endif +