*** empty log message ***
[yaffs/.git] / yaffs_guts.c
index 9ddabf687170d561ea5a87fbed323b82527540c9..bb714e01bcc79b6bc5df98f490545b8556a7fbcb 100644 (file)
@@ -14,7 +14,7 @@
  */
  //yaffs_guts.c
 
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.15 2002-12-13 00:13:06 charles Exp $";
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.16 2003-01-14 23:15:29 charles Exp $";
 
 #include "yportenv.h"
 
@@ -72,7 +72,7 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkIn
 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, int force);
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND);
 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);
@@ -90,7 +90,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 void yaffs_AbortHalfCreatedObject(yaffs_Object *obj);
 
 static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1);
 
@@ -113,12 +113,63 @@ static int yaffs_CheckFileSanity(yaffs_Object *in);
 static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);
 static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);
 
+// Chunk bitmap manipulations
 
+static __inline __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
+{
+       if(blk < dev->startBlock || blk > dev->endBlock)
+       {
+               T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),blk));
+               YBUG();
+       }
+       return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->startBlock));
+}
+
+static __inline__ void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
+{
+       __u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+        memset(blkBits,0,dev->chunkBitmapStride);
+}
+
+static __inline__ void yaffs_ClearChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+       __u8 *blkBits = yaffs_BlockBits(dev,blk);
+       
+       blkBits[chunk/8] &=  ~ (1<<(chunk & 7));
+}
+
+static __inline__ void yaffs_SetChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+       __u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+       blkBits[chunk/8] |=   (1<<(chunk & 7));
+}
+
+static __inline__ int yaffs_CheckChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+       __u8 *blkBits = yaffs_BlockBits(dev,blk);
+       return (blkBits[chunk/8] &  (1<<(chunk & 7))) ? 1 :0;
+}
+
+static __inline__ int yaffs_StillSomeChunkBits(yaffs_Device *dev,int blk)
+{
+       __u8 *blkBits = yaffs_BlockBits(dev,blk);
+       int i;
+       for(i = 0; i < dev->chunkBitmapStride; i++)
+       {
+               if(*blkBits) return 1;
+               blkBits++;
+       }
+       return 0;
+}
+
+// Function to manipulate block info
 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));
+               T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),blk));
                YBUG();
        }
        return &dev->blockInfo[blk - dev->startBlock];
@@ -154,11 +205,7 @@ static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND
        return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
 }
 
-struct nandspare {
-       yaffs_Spare     spare;
-       int             eccres1;
-       int             eccres2;
-};
+
 
 int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
                                                        int chunkInNAND, 
@@ -169,12 +216,9 @@ int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
        int retVal;
        yaffs_Spare localSpare;
 
-#ifndef CONFIG_YAFFS_USE_NANDECC
        __u8 calcEcc[3];
        int eccResult1,eccResult2;
-#else
-       struct nandspare nspare;
-#endif 
+       struct yaffs_NANDSpare nspare;
 
        dev->nPageReads++;
        
@@ -189,7 +233,8 @@ int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
        }
        
 
-#ifndef CONFIG_YAFFS_USE_NANDECC
+       if(!dev->useNANDECC)
+       {
                retVal  = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
                if(data && doErrorCorrection)
                {
@@ -228,37 +273,39 @@ int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
                                yaffs_HandleReadDataError(dev,chunkInNAND);
                        }
                }
-#else
-       retVal  = dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare*)&nspare);
-       memcpy (spare, &nspare, sizeof(yaffs_Spare));
-       if(data && doErrorCorrection)
-       {
-                if(nspare.eccres1>0)
-                {
-                       T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
-                }
-                else if(nspare.eccres1<0)
-                {
-                       T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
-                }
-
-                if(nspare.eccres2>0)
-                {
-                       T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
-                }
-                else if(nspare.eccres2<0)
-                {
-                       T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
-                }
-
-                if(nspare.eccres2 || nspare.eccres2)
-                {
-                       // Hoosterman, we had a data problem on this page
-                       yaffs_HandleReadDataError(dev,chunkInNAND);
-                }
+       }
+       else
+       {
+               retVal  = dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare*)&nspare);
+               memcpy (spare, &nspare, sizeof(yaffs_Spare));
+               if(data && doErrorCorrection)
+               {
+                       if(nspare.eccres1>0)
+                       {
+                               T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+                       }
+                       else if(nspare.eccres1<0)
+                       {
+                               T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+                       }
 
+                       if(nspare.eccres2>0)
+                       {
+                               T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+                       }
+                       else if(nspare.eccres2<0)
+                       {
+                               T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+                       }
+
+                       if(nspare.eccres2 || nspare.eccres2)
+                       {
+                               // Hoosterman, we had a data problem on this page
+                               yaffs_HandleReadDataError(dev,chunkInNAND);
+                       }
+
+               }
        }
-#endif
        return retVal;
 }
 
@@ -448,7 +495,7 @@ static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
        // Mark the block for retirement
        yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
        // Delete the chunk
-       yaffs_DeleteChunk(dev,chunkInNAND);
+       yaffs_DeleteChunk(dev,chunkInNAND,1);
 }
 
 
@@ -505,7 +552,8 @@ static __u16 yaffs_CalcNameSum(const char *name)
        {
                while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))
                {
-#ifdef CONFIG_YAFFS_WINCE
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
                        sum += toupper(*bname) * i;
 #else
                        sum += (*bname) * i;
@@ -639,9 +687,15 @@ static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)
     for(i = 0; i < nTnodes - 1; i++)
     {
        newTnodes[i].internal[0] = &newTnodes[i+1];
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+       newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
     }
        
        newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+       newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
        dev->freeTnodes = newTnodes;
        dev->nFreeTnodes+= nTnodes;
        dev->nTnodesCreated += nTnodes;
@@ -684,6 +738,13 @@ static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
        if(dev->freeTnodes)
        {
                tn = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+       if(tn->internal[YAFFS_NTNODES_INTERNAL] != 1)
+               {
+                       // Hoosterman, this thing looks like it isn't in the list
+                               T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 1" TENDSTR)));
+               }
+#endif
                dev->freeTnodes = dev->freeTnodes->internal[0];
                dev->nFreeTnodes--;
                // zero out
@@ -698,9 +759,20 @@ static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
 // FreeTnode frees up a tnode and puts it back on the free list
 static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
 {
-       tn->internal[0] = dev->freeTnodes;
-       dev->freeTnodes = tn;
-       dev->nFreeTnodes++;
+       if(tn)
+       {
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+       if(tn->internal[YAFFS_NTNODES_INTERNAL] != 0)
+               {
+                       // Hoosterman, this thing looks like it is already in the list
+                               T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 2" TENDSTR)));
+               }
+               tn->internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+               tn->internal[0] = dev->freeTnodes;
+               dev->freeTnodes = tn;
+               dev->nFreeTnodes++;
+       }
 }
 
 
@@ -970,7 +1042,9 @@ static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, in
                }
                else if(level == 0)
                {
-                       for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) //NB Don't apply the limit here, always delete a whole level0
+                       int hitLimit = 0;
+                       
+                       for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0 && !hitLimit; i--)
                        {
                            if(tn->level0[i])
                        {
@@ -998,11 +1072,15 @@ static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, in
                                        
                                        if(found)
                                        {
-                                               yaffs_DeleteChunk(in->myDev,theChunk);
+                                               yaffs_DeleteChunk(in->myDev,theChunk,1);
                                                in->nDataChunks--;
                                                if(limit)
                                                { 
                                                        *limit = *limit-1;
+                                                       if(limit <= 0) 
+                                                       { 
+                                                               hitLimit = 1;
+                                                       }
                                                }
                                        
                                        }
@@ -1011,7 +1089,7 @@ static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, in
                            }
                    
                        }
-                       return 1;
+                       return (i < 0) ? 1 : 0;
 
                        
                }
@@ -1022,6 +1100,100 @@ static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, in
        
 }
 
+// SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
+// All soft deleting does is increment the block's softdelete count and pulls the chunk out
+// of the tnode.
+// THus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
+//
+static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)
+{
+       int i;
+       int chunkInInode;
+       int theChunk;
+       yaffs_BlockInfo *theBlock;
+       yaffs_Tags tags;
+       int found;
+       int chunkDeleted;
+       int allDone = 1;
+       
+       
+       if(tn)
+       {
+               if(level > 0)
+               {
+               
+                       for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
+                       {
+                           if(tn->internal[i])
+                       {
+                                               allDone = yaffs_SoftDeleteWorker(in,tn->internal[i],level - 1,
+                                                                               (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i);
+                                       if(allDone)
+                                       {
+                                               yaffs_FreeTnode(in->myDev,tn->internal[i]);
+                                       tn->internal[i] = NULL;
+                                       }
+                                       else
+                                       {
+                                               //Hoosterman... how could this happen.
+                                       }                           
+                               }                   
+                       }
+                       return (allDone) ? 1 : 0;
+               }
+               else if(level == 0)
+               {
+                       
+                       for(i = YAFFS_NTNODES_LEVEL0 -1; i >=0; i--)
+                       {
+                           if(tn->level0[i])
+                       {
+                                       
+                                       theChunk =  (tn->level0[i] << in->myDev->chunkGroupBits);
+                                       theBlock =      yaffs_GetBlockInfo(in->myDev,  theChunk/ YAFFS_CHUNKS_PER_BLOCK);
+                                       if(theBlock)
+                                       {
+                                               theBlock->softDeletions++;
+                                       }
+                               tn->level0[i] = 0;
+                           }
+                   
+                       }
+                       return 1;
+                       
+               }
+               
+       }
+       
+       return 1;
+               
+}
+
+
+
+static void yaffs_SoftDeleteFile(yaffs_Object *obj)
+{
+       if(obj->deleted &&
+          obj->variantType == YAFFS_OBJECT_TYPE_FILE &&
+          !obj->softDeleted)
+       {
+               if(obj->nDataChunks <= 0)
+               {
+                               // Empty file, just delete it immediately
+                               yaffs_FreeTnode(obj->myDev,obj->variant.fileVariant.top);
+                               obj->variant.fileVariant.top = NULL;
+                               T(YAFFS_TRACE_TRACING,(TSTR("yaffs: Deleting empty file %d" TENDSTR),obj->objectId));
+                               yaffs_DoGenericObjectDeletion(obj);     
+               }
+               else
+               {
+                       yaffs_SoftDeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0);
+                       obj->softDeleted = 1;
+               }
+       }
+}
+
+
 
 
 
@@ -1148,7 +1320,7 @@ static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
     // Hook them into the free list
     for(i = 0; i < nObjects - 1; i++)
     {
-       (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1];
+       newObjects[i].siblings.next = (struct list_head *)(&newObjects[i+1]);
     }
        
        newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
@@ -1263,7 +1435,7 @@ static void yaffs_FreeObject(yaffs_Object *tn)
        yaffs_UnhashObject(tn);
        
        // Link into the free list.
-       (yaffs_Object *)(tn->siblings.next) = dev->freeObjects;
+       tn->siblings.next = (struct list_head *)(dev->freeObjects);
        dev->freeObjects = tn;
        dev->nFreeObjects++;
 }
@@ -1581,11 +1753,11 @@ yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
                                break;
                }
 
-               if(yaffs_GetNumberOfFreeChunks(dev) <= 0 ||
+               if(/*yaffs_GetNumberOfFreeChunks(dev) <= 0 || */
                   yaffs_UpdateObjectHeader(in,name,0) < 0)
                {
                        // Could not create the object header, fail the creation
-                       yaffs_UnlinkWorker(in);
+                       yaffs_AbortHalfCreatedObject(in);
                        in = NULL;
                }
 
@@ -1675,7 +1847,7 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *
        yaffs_Object *obj;
        int force = 0;
        
-#ifdef CONFIG_YAFFS_WINCE
+#ifdef YAFFS_CASE_INSENSITIVE
        // 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
@@ -1790,11 +1962,16 @@ static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)
        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)
+       // Set up dynamic blockinfo stuff.
+       dev->chunkBitmapStride = (dev->nChunksPerBlock+7)/8;
+       dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
+       if(dev->blockInfo && dev->chunkBits)
        {
                memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));
+               memset(dev->chunkBits,0,dev->chunkBitmapStride * nBlocks);
                return YAFFS_OK;
        }
+       
        return YAFFS_FAIL;
        
 }
@@ -1802,6 +1979,9 @@ static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)
 static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
 {
        YFREE(dev->blockInfo);
+       dev->blockInfo = NULL;
+       YFREE(dev->chunkBits);
+       dev->chunkBits = NULL;
 }
 
 // FindDiretiestBlock is used to select the dirtiest block (or close enough)
@@ -1814,7 +1994,7 @@ static int yaffs_FindDirtiestBlock(yaffs_Device *dev)
        
        int i;
        int dirtiest = -1;
-       int pagesInUse = 100; // silly big number
+       int pagesInUse = dev->nChunksPerBlock; 
        yaffs_BlockInfo *bi;
        
        for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
@@ -1834,10 +2014,10 @@ static int yaffs_FindDirtiestBlock(yaffs_Device *dev)
                bi = yaffs_GetBlockInfo(dev,b);
                
                if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
-                  bi->pagesInUse < pagesInUse)
+                  (bi->pagesInUse - bi->softDeletions )< pagesInUse)
                {
                        dirtiest = b;
-                       pagesInUse = bi->pagesInUse;
+                       pagesInUse = (bi->pagesInUse - bi->softDeletions);
                }
        }
        
@@ -1868,10 +2048,12 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
        
        if( erasedOk )
        {
+               // Clean it up...
                bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
                dev->nErasedBlocks++;
                bi->pagesInUse = 0;
-               bi->pageBits = 0;
+               bi->softDeletions = 0;
+               yaffs_ClearChunkBits(dev,blockNo);
        
                T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));
        }
@@ -1886,13 +2068,14 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
 static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
 {
        int i;
+       
        yaffs_BlockInfo *bi;
        
        if(dev->nErasedBlocks < 1)
        {
                // Hoosterman we've got a problem.
                // Can't get space to gc
-               T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during gc" TENDSTR)));
+               T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during gc" TENDSTR)));
 
                return -1;
        }
@@ -1902,13 +2085,13 @@ static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
        for(i = dev->startBlock; i <= dev->endBlock; i++)
        {
                dev->allocationBlockFinder++;
-               if(dev->allocationBlockFinder < dev->startBlock || 
-                  dev->allocationBlockFinder > dev->endBlock)
+               if(dev->allocationBlockFinder <dev->startBlock || dev->allocationBlockFinder> dev->endBlock) 
                {
-                  dev->allocationBlockFinder = dev->startBlock;
+                       dev->allocationBlockFinder = dev->startBlock;
                }
                
                bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
+
                if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
                {
                        bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
@@ -1948,7 +2131,7 @@ static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
                retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) + 
                                  dev->allocationPage;
                bi->pagesInUse++;
-               bi->pageBits |= (1 << (dev->allocationPage));
+               yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
 
                dev->allocationPage++;
                
@@ -1984,29 +2167,30 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
 {
        int oldChunk;
        int newChunk;
-       __u32 mask;
+       int chunkInBlock;
+       int markNAND;
        
        
        yaffs_Spare spare;
        yaffs_Tags  tags;
                __u8  buffer[YAFFS_BYTES_PER_CHUNK];
        
-       yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
+//     yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
        
        yaffs_Object *object;
 
        //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));        
        
-       for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK; 
-           mask && bi->pageBits;
-           mask <<= 1, oldChunk++ )
+       for(chunkInBlock = 0,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK; 
+           chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
+           chunkInBlock++, oldChunk++ )
        {
-               if(bi->pageBits & mask)
+               if(yaffs_CheckChunkBit(dev,block,chunkInBlock))
                {
                        
-                       // This page is in use and needs to be copied off
+                       // This page is in use and might need to be copied off
                        
-                       dev->nGCCopies++;
+                       markNAND = 1;
                        
                        //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
                        
@@ -2023,15 +2207,27 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
                                // No need to copy this, just forget about it and fix up the
                                // object.
                                
-                               yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); 
+                               //yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); 
                                object->nDataChunks--;
+                               
+                               if(object->nDataChunks <= 0)
+                               {
+                                       // Time to delete the file too
+                                       yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+                                       object->variant.fileVariant.top = NULL;
+                                       T(YAFFS_TRACE_TRACING,(TSTR("yaffs: About to finally delete object %d" TENDSTR),object->objectId));
+                                       yaffs_DoGenericObjectDeletion(object);                                  
+                               }
+                               markNAND = 0;
                        }
                        else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)
                        {
                                // Deleted object header with no data chunks.
-                               // Can be discarded
+                               // Can be discarded and the file deleted.
                                object->chunkId = 0;
-                               //Todo some clean up
+                               yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+                               object->variant.fileVariant.top = NULL;
+                               yaffs_DoGenericObjectDeletion(object);
                                
                        }
                        else if(object)
@@ -2043,6 +2239,7 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
                                tags.serialNumber++;
                                yaffs_LoadTagsIntoSpare(&spare,&tags);
 
+                               dev->nGCCopies++;
 
                                newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
                        
@@ -2065,7 +2262,7 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
                                }
                        }
                        
-                       yaffs_DeleteChunk(dev,oldChunk);                        
+                       yaffs_DeleteChunk(dev,oldChunk,markNAND);                       
                        
                }
        }
@@ -2076,12 +2273,12 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
 
 static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
 {
-       // todo find a file to delete
+       // 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.
+       //Scan the unlinked files looking for one to delete
        list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
        {
                l = list_entry(i, yaffs_Object,siblings);
@@ -2093,6 +2290,7 @@ static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
        return NULL;
 }
 
+
 static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
 {
        // This does background deletion on unlinked files.. only deleted ones.
@@ -2110,18 +2308,8 @@ static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
                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
-       
+               limit = -1;
+
                delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);
                
                if(obj->nDataChunks == 0)
@@ -2129,6 +2317,7 @@ static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
                        // Done all the deleting of data chunks.
                        // Now dump the header and clean up
                        yaffs_FreeTnode(dev,obj->variant.fileVariant.top);
+                       obj->variant.fileVariant.top = NULL;
                        yaffs_DoGenericObjectDeletion(obj);
                        dev->nDeletedFiles--;
                        dev->nUnlinkedFiles--;
@@ -2144,7 +2333,7 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
 {
        int block;
        
-       yaffs_DoUnlinkedFileDeletion(dev);
+       //yaffs_DoUnlinkedFileDeletion(dev);
        
        if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))
        {
@@ -2261,12 +2450,10 @@ static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, con
        
        yaffs_SpareInitialise(&spare);
        
-#ifndef CONFIG_YAFFS_USE_NANDECC
-       if(buffer)
+       if(!dev->useNANDECC && buffer)
        {
                yaffs_CalcECC(buffer,&spare);
        }
-#endif
        
        yaffs_LoadTagsIntoSpare(&spare,tags);
        
@@ -2290,13 +2477,10 @@ static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buff
        
        yaffs_SpareInitialise(&spare);
        
-#ifndef CONFIG_YAFFS_USE_NANDECC
-
-       if(buffer)
+       if(!dev->useNANDECC && buffer)
        {
                yaffs_CalcECC(buffer,&spare);
        }
-#endif
        
        yaffs_LoadTagsIntoSpare(&spare,tags);
        
@@ -2523,7 +2707,7 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkIn
                        {
                                //Hoosterman - how did this happen?
                                
-                               T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: existing chunk < 0 in scan" TENDSTR)));
+                               T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: existing chunk < 0 in scan" TENDSTR)));
 
                        }
 
@@ -2539,13 +2723,13 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkIn
                        {
                                // Use new
                                // Delete the old one and drop through to update the tnode
-                               yaffs_DeleteChunk(dev,existingChunk);
+                               yaffs_DeleteChunk(dev,existingChunk,1);
                        }
                        else
                        {
                                // Use existing.
                                // Delete the new one and return early so that the tnode isn't changed
-                               yaffs_DeleteChunk(dev,chunkInNAND);
+                               yaffs_DeleteChunk(dev,chunkInNAND,1);
                                return YAFFS_OK;
                        }
                }
@@ -2580,7 +2764,7 @@ int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffe
 }
 
 
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND)
 {
        int block;
        int page;
@@ -2588,16 +2772,26 @@ static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
        yaffs_BlockInfo *bi;
        
        if(chunkId <= 0) return;        
-               
+       
+       dev->nDeletions++;
        block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
        page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
-       yaffs_SpareInitialise(&spare);
        
-       spare.pageStatus = 0; // To mark it as deleted.
+       if(markNAND)
+       {
+               yaffs_SpareInitialise(&spare);
+       
+               spare.pageStatus = 0; // To mark it as deleted.
 
        
-       yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
-       yaffs_HandleUpdateChunk(dev,chunkId,&spare);
+               yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
+               yaffs_HandleUpdateChunk(dev,chunkId,&spare);
+       }
+       else
+       {
+                       dev->nUnmarkedDeletions++;
+       }       
+       
        bi = yaffs_GetBlockInfo(dev,block);
                        
        
@@ -2608,7 +2802,7 @@ static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
        {
                dev->nFreeChunks++;
 
-               bi->pageBits &= ~(1 << page);
+               yaffs_ClearChunkBit(dev,block,page);
                bi->pagesInUse--;
                
                if(bi->pagesInUse == 0 &&
@@ -2664,7 +2858,7 @@ int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *
                
                if(prevChunkId >= 0)
                {
-                       yaffs_DeleteChunk(dev,prevChunkId);
+                       yaffs_DeleteChunk(dev,prevChunkId,1);
        
                }
                
@@ -2791,7 +2985,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)
        
 
                // Create new chunk in NAND
-               newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);
+               newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags, (prevChunkId >= 0) ? 1 : 0 );
     
                if(newChunkId >= 0)
                {
@@ -2800,7 +2994,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)
                
                        if(prevChunkId >= 0)
                        {
-                               yaffs_DeleteChunk(dev,prevChunkId);
+                               yaffs_DeleteChunk(dev,prevChunkId,1);
                        }
                
                        in->dirty = 0;
@@ -2823,7 +3017,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)
 //  need a very intelligent search.
 
 
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
+
 
 
 static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
@@ -2834,50 +3028,55 @@ static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
        yaffs_ChunkCache *cache;
        int chunkWritten;
        int nBytes;
+       int nCaches = obj->myDev->nShortOpCaches;
        
-       do{
-               cache = NULL;
+       if  (nCaches > 0)
+       {
+               do{
+                       cache = NULL;
                
-               // 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)
+                       // Find the dirty cache for this object with the lowest chunk id.
+                       for(i = 0; i < nCaches; i++)
                        {
-                               if(!cache ||  dev->srCache[i].chunkId < lowest)
+                               if(dev->srCache[i].object == obj &&
+                               dev->srCache[i].dirty)
                                {
-                                       cache = &dev->srCache[i];
-                                       lowest = cache->chunkId;
+                                       if(!cache ||  dev->srCache[i].chunkId < lowest)
+                                       {
+                                               cache = &dev->srCache[i];
+                                               lowest = cache->chunkId;
+                                       }
                                }
                        }
-               }
                
-               if(cache)
-               {
-                       //Write it out
-
-                       nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
-                       
-                       if(nBytes > YAFFS_BYTES_PER_CHUNK)
+                       if(cache)
                        {
-                               nBytes= YAFFS_BYTES_PER_CHUNK;
-                       }
+                               //Write it out
+
+#if 0
+                               nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
                        
-                       chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
-                                                                                                               cache->chunkId,
-                                                                                                               cache->data,
-                                                                                                               nBytes,1);
+                               if(nBytes > YAFFS_BYTES_PER_CHUNK)
+                               {
+                                       nBytes= YAFFS_BYTES_PER_CHUNK;
+                               }
+#endif                 
+                               chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
+                                                                                                                       cache->chunkId,
+                                                                                                                       cache->data,
+                                                                                                                       cache->nBytes,1);
 
-                       cache->dirty = 0;
-               }
+                               cache->dirty = 0;
+                       }
                
-       } while(cache && chunkWritten > 0);
+               } 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)));
+               if(cache)
+               {
+                       //Hoosterman, disk full while writing cache out.
+                       T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
 
+               }
        }       
                
 }
@@ -2893,32 +3092,39 @@ static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)
        int usage;
        int theOne;
        
-       for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
+       if(dev->nShortOpCaches > 0)
        {
-               if(!dev->srCache[i].object)
+               for(i = 0; i < dev->nShortOpCaches; i++)
                {
-                       //T(("Grabbing empty %d\n",i));
+                       if(!dev->srCache[i].object)
+                       {
+                               //T(("Grabbing empty %d\n",i));
                        
-                       return &dev->srCache[i];
+                               return &dev->srCache[i];
+                       }
                }
-       }
        
-       theOne = -1; 
-       usage = 0; // just to stop the compiler grizzling
+               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))
+               for(i = 0; i < dev->nShortOpCaches; i++)
                {
-                       usage = dev->srCache[i].lastUse;
-                       theOne = 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;
+               //T(("Grabbing non-empty %d\n",theOne));
+               return  theOne >= 0 ?  &dev->srCache[theOne] : NULL;
+       }
+       else
+       {
+               return NULL;
+       }
        
 }
 
@@ -2930,37 +3136,42 @@ static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
        int usage;
        int i;
        
-       // Try find a non-dirty one...
+       if(dev->nShortOpCaches > 0)
+       {
+               // Try find a non-dirty one...
        
-       cache = yaffs_GrabChunkCacheWorker(dev);
+               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.
+               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;
+                       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)
+                       for(i = 1; i < dev->nShortOpCaches; i++)
                        {
-                               usage  = dev->srCache[i].lastUse;
-                               theObj = dev->srCache[i].object;
+                               if( dev->srCache[i].object && 
+                                       dev->srCache[i].lastUse < usage)
+                               {
+                                       usage  = dev->srCache[i].lastUse;
+                                       theObj = dev->srCache[i].object;
+                               }
                        }
-               }
                
-               yaffs_FlushFilesChunkCache(theObj);
+                       yaffs_FlushFilesChunkCache(theObj);
                
-               // Try again
-               cache = yaffs_GrabChunkCacheWorker(dev);
-       }
+                       // Try again
+                       cache = yaffs_GrabChunkCacheWorker(dev);
+               }
        
-       return cache;
+               return cache;
+       }
+       else
+               return NULL;
 
 }
 
@@ -2970,43 +3181,48 @@ static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunk
 {
        yaffs_Device *dev = obj->myDev;
        int i;
-       
-       for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)
+       if(dev->nShortOpCaches > 0)
        {
-               if(dev->srCache[i].object == obj && 
-                  dev->srCache[i].chunkId == chunkId)
+               for(i = 0; i < dev->nShortOpCaches; i++)
                {
-                       dev->cacheHits++;
+                       if(dev->srCache[i].object == obj && 
+                       dev->srCache[i].chunkId == chunkId)
+                       {
+                               dev->cacheHits++;
                        
-                       return &dev->srCache[i];
+                               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)
+
+       if(dev->nShortOpCaches > 0)
        {
-               // Reset the cache usages
-               int i;
-               for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)
+               if( dev->srLastUse < 0 || 
+                       dev->srLastUse > 100000000)
                {
-                       dev->srCache[i].lastUse = 0;
+                       // Reset the cache usages
+                       int i;
+                       for(i = 1; i < dev->nShortOpCaches; i++)
+                       {
+                               dev->srCache[i].lastUse = 0;
+                       }
+                       dev->srLastUse = 0;
                }
-               dev->srLastUse = 0;
-       }
 
-       dev->srLastUse++;
+               dev->srLastUse++;
        
-       cache->lastUse = dev->srLastUse;
+               cache->lastUse = dev->srLastUse;
 
-       if(isAWrite)
-       {
-               cache->dirty = 1;
+               if(isAWrite)
+               {
+                       cache->dirty = 1;
+               }
        }
 }
 
@@ -3015,11 +3231,14 @@ static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int
 // 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)
+       if(object->myDev->nShortOpCaches > 0)
        {
-               cache->object = NULL;
+               yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
+
+               if(cache)
+               {
+                       cache->object = NULL;
+               }
        }
 }
 
@@ -3031,49 +3250,20 @@ 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)
+       if(dev->nShortOpCaches > 0)
+       { 
+               // Now invalidate it.
+               for(i = 0; i < dev->nShortOpCaches; i++)
                {
-                       dev->srCache[i].object = NULL;
+                       if(dev->srCache[i].object == in)
+                       {
+                               dev->srCache[i].object = NULL;
+                       }
                }
        }
 }
 
 
-#else
-// Stubs for disabling short op caching
-
-static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
-{}
-
-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
 
 
 
@@ -3119,27 +3309,32 @@ 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)
+                       if(dev->nShortOpCaches > 0)
                        {
-                               cache = yaffs_GrabChunkCache(in->myDev);
-                               cache->object = in;
-                               cache->chunkId = chunk;
-                               cache->dirty = 0;
-                               yaffs_ReadChunkDataFromObject(in,chunk,cache->data);            
-                       }
+                               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);
+                                       cache->nBytes = 0;      
+                               }
                        
-                       yaffs_UseChunkCache(dev,cache,0);
+                               yaffs_UseChunkCache(dev,cache,0);
+
+                               memcpy(buffer,&cache->data[start],nToCopy);
+                       }
+                       else
+                       {
+                               // Read into the local buffer then copy...
+                               yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);               
+                               memcpy(buffer,&dev->localBuffer[start],nToCopy);
+                       }
 
-                       memcpy(buffer,&cache->data[start],nToCopy);
-#else
-                       // Read into the local buffer then copy...
-                       yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);               
-                       memcpy(buffer,&dev->localBuffer[start],nToCopy);
-#endif
                }
                else
                {
@@ -3221,42 +3416,44 @@ 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))
+                       if(dev->nShortOpCaches > 0)
                        {
-                               cache = yaffs_GrabChunkCache(in->myDev);
-                               cache->object = in;
-                               cache->chunkId = chunk;
-                               cache->dirty = 0;
-                               yaffs_ReadChunkDataFromObject(in,chunk,cache->data);            
-                       }
+                               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(dev,cache,1);
-                               memcpy(&cache->data[start],buffer,nToCopy);
+                               if(cache)
+                               {       
+                                       yaffs_UseChunkCache(dev,cache,1);
+                                       memcpy(&cache->data[start],buffer,nToCopy);
+                                       cache->nBytes = nToWriteBack;
+                               }
+                               else
+                               {
+                                       chunkWritten = -1; // fail the write
+                               }
                        }
                        else
                        {
-                               chunkWritten = -1; // fail the write
-                       }
-#else
-               
-
-                       // An incomplete start or end chunk (or maybe both start and end chunk)
-                       // Read into the local buffer then copy, then copy over and write back.
+                               // An incomplete start or end chunk (or maybe both start and end chunk)
+                               // Read into the local buffer then copy, then copy over and write back.
                
-                       yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+                               yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
                                
-                       memcpy(&dev->localBuffer[start],buffer,nToCopy);
+                               memcpy(&dev->localBuffer[start],buffer,nToCopy);
                        
-                       chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,nToWriteBack,0);
+                               chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,nToWriteBack,0);
                        
-                       //T(("Write with readback to chunk %d %d  start %d  copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
-#endif
+                               //T(("Write with readback to chunk %d %d  start %d  copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
+                       }
                        
                }
                else
@@ -3341,7 +3538,7 @@ int yaffs_ResizeFile(yaffs_Object *in, int newSize)
                        else
                        {
                                in->nDataChunks--;
-                               yaffs_DeleteChunk(dev,chunkId);
+                               yaffs_DeleteChunk(dev,chunkId,1);
                        }
                }
                
@@ -3391,7 +3588,7 @@ loff_t yaffs_GetFileSize(yaffs_Object *obj)
 // yaffs_FlushFile() updates the file's
 // objectId in NAND
 
-int yaffs_FlushFile(yaffs_Object *in)
+int yaffs_FlushFile(yaffs_Object *in, int updateTime)
 {
        int retVal;
        if(in->dirty)
@@ -3399,12 +3596,14 @@ int yaffs_FlushFile(yaffs_Object *in)
                //T(("flushing object header\n"));
                
                yaffs_FlushFilesChunkCache(in);
-       
+               if(updateTime)
+               {
 #ifdef CONFIG_YAFFS_WINCE
-               yfsd_WinFileTimeNow(in->win_mtime);
+                       yfsd_WinFileTimeNow(in->win_mtime);
 #else
-               in->st_mtime = CURRENT_TIME;
+                       in->st_mtime = CURRENT_TIME;
 #endif
+               }
 
                retVal = yaffs_UpdateObjectHeader(in,NULL,0);
        }
@@ -3425,7 +3624,7 @@ static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
        yaffs_InvalidateWholeChunkCache(in);
        
        yaffs_RemoveObjectFromDirectory(in);
-       yaffs_DeleteChunk(in->myDev,in->chunkId);
+       yaffs_DeleteChunk(in->myDev,in->chunkId,1);
 #ifdef __KERNEL__
        if(in->myInode)
        {
@@ -3445,6 +3644,7 @@ static int yaffs_UnlinkFile(yaffs_Object *in)
 {
 
 #ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION
+
        // Delete the file data & tnodes
 
         yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
@@ -3457,7 +3657,10 @@ static int yaffs_UnlinkFile(yaffs_Object *in)
        int retVal;
        int immediateDeletion=0;
        retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);
-       if(retVal == YAFFS_OK)
+       if(1 || // Ignore the result of the change name. This will only fail
+                       // if the disk was completely full (an error condition)
+                       // We ignore the error so we can delete files to recover the disk
+          retVal == YAFFS_OK)
        {
                //in->unlinked = 1;
                //in->myDev->nUnlinkedFiles++;
@@ -3468,8 +3671,7 @@ static int yaffs_UnlinkFile(yaffs_Object *in)
                        immediateDeletion = 1;
 
                }
-#endif
-#ifdef CONFIG_YAFFS_WINCE
+#else
                if(in->inUse <= 0)
                {
                        immediateDeletion = 1;
@@ -3482,6 +3684,7 @@ static int yaffs_UnlinkFile(yaffs_Object *in)
                        T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
                        in->deleted=1;
                        in->myDev->nDeletedFiles++;
+                       yaffs_SoftDeleteFile(in);
                }
        
        }
@@ -3494,18 +3697,33 @@ static int yaffs_UnlinkFile(yaffs_Object *in)
 int yaffs_DeleteFile(yaffs_Object *in)
 {
        int retVal = YAFFS_OK;
-       if(!in->unlinked)
+       
+       if(in->nDataChunks > 0)
        {
-               retVal = yaffs_UnlinkFile(in);
+               // Use soft deletion
+               if(!in->unlinked)
+               {
+                       retVal = yaffs_UnlinkFile(in);
+               }
+               if(retVal == YAFFS_OK && 
+               in->unlinked &&
+               !in->deleted)
+               {
+                       in->deleted = 1;
+                       in->myDev->nDeletedFiles++;
+                       yaffs_SoftDeleteFile(in);
+               }
+               return in->deleted ? YAFFS_OK : YAFFS_FAIL;     
        }
-       if(retVal == YAFFS_OK && 
-          in->unlinked &&
-          !in->deleted)
+       else
        {
-               in->deleted = 1;
-               in->myDev->nDeletedFiles++;
+               // The file has no data chunks so we toss it immediately
+               yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
+               in->variant.fileVariant.top = NULL;
+               yaffs_DoGenericObjectDeletion(in);      
+               
+               return YAFFS_OK;        
        }
-       return in->deleted ? YAFFS_OK : YAFFS_FAIL;     
 }
 
 static int yaffs_DeleteDirectory(yaffs_Object *in)
@@ -3536,6 +3754,20 @@ static int yaffs_DeleteHardLink(yaffs_Object *in)
 }
 
 
+static void yaffs_AbortHalfCreatedObject(yaffs_Object *obj)
+{
+       switch(obj->variantType)
+       {
+               case YAFFS_OBJECT_TYPE_FILE: yaffs_DeleteFile(obj); break;
+               case YAFFS_OBJECT_TYPE_DIRECTORY: yaffs_DeleteDirectory(obj); break;
+               case YAFFS_OBJECT_TYPE_SYMLINK: yaffs_DeleteSymLink(obj); break;
+               case YAFFS_OBJECT_TYPE_HARDLINK: yaffs_DeleteHardLink(obj); break;
+               case YAFFS_OBJECT_TYPE_SPECIAL: yaffs_DoGenericObjectDeletion(obj); break;
+               case YAFFS_OBJECT_TYPE_UNKNOWN: break; // should not happen.
+       }
+}
+
+
 static int yaffs_UnlinkWorker(yaffs_Object *obj)
 {
 
@@ -3687,8 +3919,9 @@ static int yaffs_Scan(yaffs_Device *dev)
        {
                deleted = 0;
                bi = yaffs_GetBlockInfo(dev,blk);
-               bi->pageBits = 0;
+               yaffs_ClearChunkBits(dev,blk);
                bi->pagesInUse = 0;
+               bi->softDeletions = 0;
                state = YAFFS_BLOCK_STATE_SCANNING;
                
                
@@ -3746,7 +3979,7 @@ static int yaffs_Scan(yaffs_Device *dev)
                        {
                                int endpos;
                                // A data chunk.
-                               bi->pageBits |= (1 << c);
+                               yaffs_SetChunkBit(dev,blk,c);
                                bi->pagesInUse++;
                                                                
                                in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
@@ -3769,7 +4002,7 @@ 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
-                               bi->pageBits |= (1 << c);
+                               yaffs_SetChunkBit(dev,blk,c);
                                bi->pagesInUse++;
                                                        
                                yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1);
@@ -3788,13 +4021,13 @@ static int yaffs_Scan(yaffs_Device *dev)
                                        if(((existingSerial+1) & 3) == newSerial)
                                        {
                                                // Use new one - destroy the exisiting one
-                                               yaffs_DeleteChunk(dev,in->chunkId);
+                                               yaffs_DeleteChunk(dev,in->chunkId,1);
                                                in->valid = 0;
                                        }
                                        else
                                        {
                                                // Use existing - destroy this one.
-                                               yaffs_DeleteChunk(dev,chunk);
+                                               yaffs_DeleteChunk(dev,chunk,1);
                                        }
                                }
                                
@@ -3868,7 +4101,7 @@ 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)));
+                                               T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan" TENDSTR)));
 
                                        }
                                
@@ -3896,7 +4129,7 @@ static int yaffs_Scan(yaffs_Device *dev)
                                                        break;
                                                case YAFFS_OBJECT_TYPE_HARDLINK:
                                                        in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
-                                                       (yaffs_Object *)(in->hardLinks.next) = hardList;
+                                                       in->hardLinks.next = (struct list_head *)hardList;
                                                        hardList = in;
                                                        break;
                                                case YAFFS_OBJECT_TYPE_DIRECTORY:       // Do nothing
@@ -3956,6 +4189,19 @@ static int yaffs_Scan(yaffs_Device *dev)
                
        }
        
+       {
+               struct list_head *i;    
+               yaffs_Object *l;
+               // Soft delete all the unlinked files
+               list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+               {
+                       l = list_entry(i, yaffs_Object,siblings);
+                       if(l->deleted)
+                       {
+                               yaffs_SoftDeleteFile(l);                
+                       }
+               }       
+       }
        
        
        return YAFFS_OK;
@@ -4291,6 +4537,14 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
                T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR)));
                return YAFFS_FAIL;
        }
+
+       if(dev->isMounted)
+       {
+               T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: device already mounted\n" TENDSTR)));
+               return YAFFS_FAIL;
+       }
+
+       dev->isMounted = 1;
        
        if(dev->startBlock <= 0 ||
           (dev->endBlock - dev->startBlock) < 10)
@@ -4332,6 +4586,10 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        }
        dev->chunkGroupSize = 1 << dev->chunkGroupBits;
        
+       // Stuff to be taken out later
+       dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+       dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+       
        
        // More device initialisation
        dev->garbageCollectionRequired  = 0;
@@ -4348,7 +4606,10 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        dev->tagsEccFixed=0;
        dev->tagsEccUnfixed=0;
        
-       dev->localBuffer = YMALLOC(YAFFS_BYTES_PER_CHUNK);
+       dev->localBuffer = YMALLOC(dev->nBytesPerChunk);
+       
+
+       
        
        yaffs_InitialiseBlocks(dev,nBlocks);
        
@@ -4356,10 +4617,18 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
 
        yaffs_InitialiseObjects(dev);
        
-#ifdef CONFIG_YAFFS_SHORT_OP_CACHE
+       if(dev->nShortOpCaches > 0)
        { 
                int i;
-               for(i=0; i < YAFFS_N_CACHE_CHUNKS; i++)
+               
+               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++)
                {
                        dev->srCache[i].object = NULL;
                        dev->srCache[i].lastUse = 0;
@@ -4367,7 +4636,6 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
                }
                dev->srLastUse = 0;
        }
-#endif
 
        dev->cacheHits = 0;
        
@@ -4400,12 +4668,18 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
 
 void yaffs_Deinitialise(yaffs_Device *dev)
 {
-       yaffs_DeinitialiseBlocks(dev);
-       yaffs_DeinitialiseTnodes(dev);
-       yaffs_DeinitialiseObjects(dev);
+       if(dev->isMounted)
+       {
+       
+               yaffs_DeinitialiseBlocks(dev);
+               yaffs_DeinitialiseTnodes(dev);
+               yaffs_DeinitialiseObjects(dev);
+       }
        
 }
 
+#if 0
+
 int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
 {
        int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);
@@ -4424,13 +4698,75 @@ int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
                        nFree += l->nDataChunks;
                }
        }
+       
+       
+       printf("___________ nFreeChunks is %d nFree is %d\n",dev->nFreeChunks,nFree);   
+
+       if(nFree < 0) nFree = 0;
+
+       return nFree;   
+       
+}
+
+#endif
+
+int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+       int nFree;
+       int pending;
+       int b;
+       
+       yaffs_BlockInfo *blk;
+       
+       struct list_head *i;    
+       yaffs_Object *l;
+       
+       for(nFree = 0, b = dev->startBlock; b <= dev->endBlock; b++)
+       {
+               blk = yaffs_GetBlockInfo(dev,b);
+               
+               switch(blk->blockState)
+               {
+                       case YAFFS_BLOCK_STATE_EMPTY:
+                       case YAFFS_BLOCK_STATE_ALLOCATING: 
+                       case YAFFS_BLOCK_STATE_FULL: nFree += (dev->nChunksPerBlock - blk->pagesInUse); break;
+                       default: break;
+               }
+       }
+       
+       
+       pending = 0;
+       
+       // 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)
+               {
+                       pending++;
+                       pending += l->nDataChunks;
+               }
+       }
+       
+       
+       
+       //printf("___________ really free is %d, pending %d, nFree is %d\n",nFree,pending, nFree+pending);
+       
+       if(nFree != dev->nFreeChunks) 
+       {
+       //      printf("___________Different! really free is %d, nFreeChunks %d\n",nFree dev->nFreeChunks);
+       }
 
+       nFree += pending;
+       
+       if(nFree < 0) nFree = 0;
 
-       return (nFree < 0) ? 0 : nFree; 
+       return nFree;   
        
 }
 
 
+
 /////////////////// YAFFS test code //////////////////////////////////
 
 #define yaffs_CheckStruct(structure,syze, name) \
@@ -4446,7 +4782,9 @@ static int yaffs_CheckStructures(void)
        yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")
        yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")
        yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")
+#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
        yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
+#endif
        yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")