Fix issue with unlinking hardlinks failing sanity check
[yaffs2.git] / yaffs_guts.c
index a51d82d6aa5d6df0aac9cde72dbab396bc2e564c..d4349c92ad2342c151f9f9755791b5751aae0bb9 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.98 2009-12-07 01:17:33 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.104 2010-01-05 22:58:15 charles Exp $";
 
 #include "yportenv.h"
 
@@ -113,6 +113,12 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,
                                        yaffs_FileStructure *fStruct,
                                        __u32 chunkId);
 
+static void yaffs_SkipRestOfBlock(yaffs_Device *dev);
+static int yaffs_VerifyChunkWritten(yaffs_Device *dev,
+                                       int chunkInNAND,
+                                       const __u8 *data,
+                                       yaffs_ExtendedTags *tags);
+
 /* Function to calculate chunk and offset */
 
 static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, int *chunkOut,
@@ -548,8 +554,8 @@ static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh,
 
        if (!(tags && obj && oh)) {
                T(YAFFS_TRACE_VERIFY,
-                               (TSTR("Verifying object header tags %x obj %x oh %x"TENDSTR),
-                               (__u32)tags, (__u32)obj, (__u32)oh));
+                               (TSTR("Verifying object header tags %p obj %p oh %p"TENDSTR),
+                               tags, obj, oh));
                return;
        }
 
@@ -918,6 +924,29 @@ static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
 
 }
 
+
+static int yaffs_VerifyChunkWritten(yaffs_Device *dev,
+                                       int chunkInNAND,
+                                       const __u8 *data,
+                                       yaffs_ExtendedTags *tags)
+{
+       int retval = YAFFS_OK;
+       yaffs_ExtendedTags tempTags;
+       __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__);
+       int result;
+       
+       result = yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND,buffer,&tempTags);
+       if(memcmp(buffer,data,dev->nDataBytesPerChunk) ||
+               tempTags.objectId != tags->objectId ||
+               tempTags.chunkId  != tags->chunkId ||
+               tempTags.byteCount != tags->byteCount)
+               retval = YAFFS_FAIL;
+
+       yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
+
+       return retval;
+}
+
 static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
                                        const __u8 *data,
                                        yaffs_ExtendedTags *tags,
@@ -957,12 +986,11 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
                 * chunk due to power loss.  This checking policy should
                 * catch that case with very few checks and thus save a
                 * lot of checks that are most likely not needed.
+                *
+                * Mods to the above
+                * If an erase check fails or the write fails we skip the 
+                * rest of the block.
                 */
-               if (bi->gcPrioritise) {
-                       yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
-                       /* try another chunk */
-                       continue;
-               }
 
                /* let's give it a try */
                attempts++;
@@ -977,20 +1005,30 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
                                (TSTR("**>> yaffs chunk %d was not erased"
                                TENDSTR), chunk));
 
-                               /* try another chunk */
+                               /* If not erased, delete this one,
+                                * skip rest of block and
+                                * try another chunk */
+                                yaffs_DeleteChunk(dev,chunk,1,__LINE__);
+                                yaffs_SkipRestOfBlock(dev);
                                continue;
                        }
-                       bi->skipErasedCheck = 1;
                }
 
                writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk,
                                data, tags);
+
+               if(!bi->skipErasedCheck)
+                       writeOk = yaffs_VerifyChunkWritten(dev, chunk, data, tags);
+
                if (writeOk != YAFFS_OK) {
+                       /* Clean up aborted write, skip to next block and
+                        * try another chunk */
                        yaffs_HandleWriteChunkError(dev, chunk, erasedOk);
-                       /* try another chunk */
                        continue;
                }
 
+               bi->skipErasedCheck = 1;
+
                /* Copy the data into the robustification buffer */
                yaffs_HandleWriteChunkOk(dev, chunk, data, tags);
 
@@ -1100,6 +1138,7 @@ static void yaffs_HandleWriteChunkError(yaffs_Device *dev, int chunkInNAND,
 
        /* Delete the chunk */
        yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
+       yaffs_SkipRestOfBlock(dev);
 }
 
 
@@ -1312,6 +1351,7 @@ static void yaffs_DeinitialiseTnodes(yaffs_Device *dev)
 
        dev->freeTnodes = NULL;
        dev->nFreeTnodes = 0;
+       dev->nTnodesCreated = 0;
 }
 
 static void yaffs_InitialiseTnodes(yaffs_Device *dev)
@@ -1768,6 +1808,10 @@ static void yaffs_SoftDeleteFile(yaffs_Object *obj)
  * level 0 tnode entries must be zeroed out.
  * Could also use this for file deletion, but that's probably better handled
  * by a special case.
+ *
+ * This function is recursive. For levels > 0 the function is called again on
+ * any sub-tree. For level == 0 we just check if the sub-tree has data.
+ * If there is no data in a subtree then it is pruned.
  */
 
 static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn,
@@ -1779,17 +1823,32 @@ static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn,
        if (tn) {
                hasData = 0;
 
-               for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {
-                       if (tn->internal[i] && level > 0) {
-                               tn->internal[i] =
-                                   yaffs_PruneWorker(dev, tn->internal[i],
-                                                     level - 1,
-                                                     (i == 0) ? del0 : 1);
+               if(level > 0){
+                       for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) {
+                               if (tn->internal[i]) {
+                                       tn->internal[i] =
+                                               yaffs_PruneWorker(dev, tn->internal[i],
+                                                       level - 1,
+                                                       (i == 0) ? del0 : 1);
+                               }
+
+                               if (tn->internal[i])
+                                       hasData++;
                        }
+               } else {
+                       int tnodeSize;
+                       __u32 *map = (__u32 *)tn;
+                       tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
 
-                       if (tn->internal[i])
-                               hasData++;
-               }
+                       if (tnodeSize < sizeof(yaffs_Tnode))
+                               tnodeSize = sizeof(yaffs_Tnode);
+                       tnodeSize /= sizeof(__u32);
+
+                        for(i = 0; !hasData && i < tnodeSize; i++){
+                                if(map[i])
+                                        hasData++;
+                        }
+                }
 
                if (hasData == 0 && del0) {
                        /* Free and return NULL */
@@ -2051,6 +2110,7 @@ static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
 
        dev->freeObjects = NULL;
        dev->nFreeObjects = 0;
+       dev->nObjectsCreated = 0;
 }
 
 static void yaffs_InitialiseObjects(yaffs_Device *dev)
@@ -2979,6 +3039,22 @@ static int yaffs_GetErasedChunks(yaffs_Device *dev)
 
 }
 
+/*
+ * yaffs_SkipRestOfBlock() skips over the rest of the allocation block
+ * if we don't want to write to it.
+ */
+static void yaffs_SkipRestOfBlock(yaffs_Device *dev)
+{
+       if(dev->allocationBlock > 0){
+               yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
+               if(bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING){
+                       bi->blockState = YAFFS_BLOCK_STATE_FULL;
+                       dev->allocationBlock = -1;
+               }
+       }
+}
+
+
 static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
                int wholeBlock)
 {
@@ -4464,8 +4540,8 @@ static int yaffs_WriteCheckpointObjects(yaffs_Device *dev)
                                        cp.structType = sizeof(cp);
 
                                        T(YAFFS_TRACE_CHECKPOINT, (
-                                               TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR),
-                                               cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk, (unsigned) obj));
+                                               TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %p" TENDSTR),
+                                               cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk, obj));
 
                                        ok = (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp));
 
@@ -4498,7 +4574,7 @@ static int yaffs_ReadCheckpointObjects(yaffs_Device *dev)
                ok = (yaffs_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp));
                if (cp.structType != sizeof(cp)) {
                        T(YAFFS_TRACE_CHECKPOINT, (TSTR("struct size %d instead of %d ok %d"TENDSTR),
-                               cp.structType, sizeof(cp), ok));
+                               cp.structType, (int)sizeof(cp), ok));
                        ok = 0;
                }
 
@@ -5327,23 +5403,26 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj)
                 * Instead, we do the following:
                 * - Select a hardlink.
                 * - Unhook it from the hard links
-                * - Unhook it from its parent directory (so that the rename can work)
+                * - Move it from its parent directory (so that the rename can work)
                 * - Rename the object to the hardlink's name.
                 * - Delete the hardlink
                 */
 
                yaffs_Object *hl;
+               yaffs_Object *parent;
                int retVal;
                YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
 
                hl = ylist_entry(obj->hardLinks.next, yaffs_Object, hardLinks);
 
+               yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1);
+               parent = hl->parent;
+
                ylist_del_init(&hl->hardLinks);
-               ylist_del_init(&hl->siblings);
 
-               yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1);
+               yaffs_AddObjectToDirectory(obj->myDev->unlinkedDir, hl);
 
-               retVal = yaffs_ChangeObjectName(obj, hl->parent, name, 0, 0);
+               retVal = yaffs_ChangeObjectName(obj,parent, name, 0, 0);
 
                if (retVal == YAFFS_OK)
                        retVal = yaffs_DoGenericObjectDeletion(hl);
@@ -5758,7 +5837,7 @@ static int yaffs_Scan(yaffs_Device *dev)
                                        dev->allocationBlock = blk;
                                        dev->allocationPage = c;
                                        dev->allocationBlockFinder = blk;
-                                       /* Set it to here to encourage the allocator to go forth from here. */
+                                       /* Set block finder here to encourage the allocator to go forth from here. */
 
                                }
 
@@ -6004,12 +6083,6 @@ static int yaffs_Scan(yaffs_Device *dev)
                                                break;
                                        }
 
-/*
-                                       if (parent == dev->deletedDir) {
-                                               yaffs_DestroyObject(in);
-                                               bi->hasShrinkHeader = 1;
-                                       }
-*/
                                }
                        }
                }
@@ -6019,6 +6092,12 @@ static int yaffs_Scan(yaffs_Device *dev)
                        state = YAFFS_BLOCK_STATE_FULL;
                }
 
+               if (state == YAFFS_BLOCK_STATE_ALLOCATING) {
+                       /* If the block was partially allocated then treat it as fully allocated.*/
+                       state = YAFFS_BLOCK_STATE_FULL;
+                       dev->allocationBlock = -1;
+               }
+
                bi->blockState = state;
 
                /* Now let's see if it was dirty */
@@ -6361,13 +6440,9 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                                                        dev->allocationBlockFinder = blk;
                                                } else {
                                                        /* This is a partially written block that is not
-                                                        * the current allocation block. This block must have
-                                                        * had a write failure, so set up for retirement.
+                                                        * the current allocation block.
                                                         */
 
-                                                        /* bi->needsRetiring = 1; ??? TODO */
-                                                        bi->gcPrioritise = 1;
-
                                                         T(YAFFS_TRACE_ALWAYS,
                                                         (TSTR("Partially written block %d detected" TENDSTR),
                                                         blk));
@@ -6629,6 +6704,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                                                                           oh->
                                                                           shadowsObject,
                                                                           1);
+                                                       
 
 
                                                yaffs_SetObjectName(in, oh->name);
@@ -6760,6 +6836,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                        state = YAFFS_BLOCK_STATE_FULL;
                }
 
+
                bi->blockState = state;
 
                /* Now let's see if it was dirty */
@@ -6770,6 +6847,8 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                }
 
        }
+       
+       yaffs_SkipRestOfBlock(dev);
 
        if (altBlockIndex)
                YFREE_ALT(blockIndex);
@@ -7766,7 +7845,7 @@ static void yaffs_VerifyFreeChunks(yaffs_Device *dev)
        do { \
                if (sizeof(structure) != syze) { \
                        T(YAFFS_TRACE_ALWAYS, (TSTR("%s should be %d but is %d\n" TENDSTR),\
-                               name, syze, sizeof(structure))); \
+                               name, syze, (int) sizeof(structure))); \
                        return YAFFS_FAIL; \
                } \
        } while (0)
@@ -7777,7 +7856,7 @@ static int yaffs_CheckStructures(void)
 /*      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");
+/*     yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode"); */
 #endif
 #ifndef CONFIG_YAFFS_WINCE
        yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader");