+ if (tn) {
+ if (level > 0) {
+
+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) {
+ if (tn->internal[i]) {
+ ok = yaffs_VerifyTnodeWorker(obj,
+ tn->internal[i],
+ level - 1,
+ (chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i);
+ }
+ }
+ } else if (level == 0) {
+ yaffs_ExtendedTags tags;
+ __u32 objectId = obj->objectId;
+
+ chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS;
+
+ for (i = 0; i < YAFFS_NTNODES_LEVEL0; i++) {
+ __u32 theChunk = yaffs_GetChunkGroupBase(dev, tn, i);
+
+ if (theChunk > 0) {
+ /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */
+ yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, &tags);
+ if (tags.objectId != objectId || tags.chunkId != chunkOffset) {
+ T(~0, (TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR),
+ objectId, chunkOffset, theChunk,
+ tags.objectId, tags.chunkId));
+ }
+ }
+ chunkOffset++;
+ }
+ }
+ }
+
+ return ok;
+
+}
+
+
+static void yaffs_VerifyFile(yaffs_Object *obj)
+{
+ int requiredTallness;
+ int actualTallness;
+ __u32 lastChunk;
+ __u32 x;
+ __u32 i;
+ yaffs_Device *dev;
+ yaffs_ExtendedTags tags;
+ yaffs_Tnode *tn;
+ __u32 objectId;
+
+ if (!obj)
+ return;
+
+ if (yaffs_SkipVerification(obj->myDev))
+ return;
+
+ dev = obj->myDev;
+ objectId = obj->objectId;
+
+ /* Check file size is consistent with tnode depth */
+ lastChunk = obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1;
+ x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS;
+ requiredTallness = 0;
+ while (x > 0) {
+ x >>= YAFFS_TNODES_INTERNAL_BITS;
+ requiredTallness++;
+ }
+
+ actualTallness = obj->variant.fileVariant.topLevel;
+
+ if (requiredTallness > actualTallness)
+ T(YAFFS_TRACE_VERIFY,
+ (TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR),
+ obj->objectId, actualTallness, requiredTallness));
+
+
+ /* Check that the chunks in the tnode tree are all correct.
+ * We do this by scanning through the tnode tree and
+ * checking the tags for every chunk match.
+ */
+
+ if (yaffs_SkipNANDVerification(dev))
+ return;
+
+ for (i = 1; i <= lastChunk; i++) {
+ tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant, i);
+
+ if (tn) {
+ __u32 theChunk = yaffs_GetChunkGroupBase(dev, tn, i);
+ if (theChunk > 0) {
+ /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */
+ yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, &tags);
+ if (tags.objectId != objectId || tags.chunkId != i) {
+ T(~0, (TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR),
+ objectId, i, theChunk,
+ tags.objectId, tags.chunkId));
+ }
+ }
+ }
+ }
+}
+
+
+static void yaffs_VerifyHardLink(yaffs_Object *obj)
+{
+ if (obj && yaffs_SkipVerification(obj->myDev))
+ return;
+
+ /* Verify sane equivalent object */
+}
+
+static void yaffs_VerifySymlink(yaffs_Object *obj)
+{
+ if (obj && yaffs_SkipVerification(obj->myDev))
+ return;
+
+ /* Verify symlink string */
+}
+
+static void yaffs_VerifySpecial(yaffs_Object *obj)
+{
+ if (obj && yaffs_SkipVerification(obj->myDev))
+ return;
+}
+
+static void yaffs_VerifyObject(yaffs_Object *obj)
+{
+ yaffs_Device *dev;
+
+ __u32 chunkMin;
+ __u32 chunkMax;
+
+ __u32 chunkIdOk;
+ __u32 chunkInRange;
+ __u32 chunkShouldNotBeDeleted;
+ __u32 chunkValid;
+
+ if (!obj)
+ return;
+
+ if (obj->beingCreated)
+ return;
+
+ dev = obj->myDev;
+
+ if (yaffs_SkipVerification(dev))
+ return;
+
+ /* Check sane object header chunk */
+
+ chunkMin = dev->internalStartBlock * dev->param.nChunksPerBlock;
+ chunkMax = (dev->internalEndBlock+1) * dev->param.nChunksPerBlock - 1;
+
+ chunkInRange = (((unsigned)(obj->hdrChunk)) >= chunkMin && ((unsigned)(obj->hdrChunk)) <= chunkMax);
+ chunkIdOk = chunkInRange || (obj->hdrChunk == 0);
+ chunkValid = chunkInRange &&
+ yaffs_CheckChunkBit(dev,
+ obj->hdrChunk / dev->param.nChunksPerBlock,
+ obj->hdrChunk % dev->param.nChunksPerBlock);
+ chunkShouldNotBeDeleted = chunkInRange && !chunkValid;
+
+ if (!obj->fake &&
+ (!chunkIdOk || chunkShouldNotBeDeleted)) {
+ T(YAFFS_TRACE_VERIFY,
+ (TSTR("Obj %d has chunkId %d %s %s"TENDSTR),
+ obj->objectId, obj->hdrChunk,
+ chunkIdOk ? "" : ",out of range",
+ chunkShouldNotBeDeleted ? ",marked as deleted" : ""));
+ }
+
+ if (chunkValid && !yaffs_SkipNANDVerification(dev)) {
+ yaffs_ExtendedTags tags;
+ yaffs_ObjectHeader *oh;
+ __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__);
+
+ oh = (yaffs_ObjectHeader *)buffer;
+
+ yaffs_ReadChunkWithTagsFromNAND(dev, obj->hdrChunk, buffer,
+ &tags);
+
+ yaffs_VerifyObjectHeader(obj, oh, &tags, 1);
+
+ yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
+ }
+
+ /* Verify it has a parent */
+ if (obj && !obj->fake &&
+ (!obj->parent || obj->parent->myDev != dev)) {
+ T(YAFFS_TRACE_VERIFY,
+ (TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR),
+ obj->objectId, obj->parent));
+ }
+
+ /* Verify parent is a directory */
+ if (obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ T(YAFFS_TRACE_VERIFY,
+ (TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR),
+ obj->objectId, obj->parent->variantType));
+ }
+
+ switch (obj->variantType) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ yaffs_VerifyFile(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ yaffs_VerifySymlink(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ yaffs_VerifyDirectory(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ yaffs_VerifyHardLink(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ yaffs_VerifySpecial(obj);
+ break;
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ default:
+ T(YAFFS_TRACE_VERIFY,
+ (TSTR("Obj %d has illegaltype %d"TENDSTR),
+ obj->objectId, obj->variantType));
+ break;
+ }
+}
+
+static void yaffs_VerifyObjects(yaffs_Device *dev)
+{
+ yaffs_Object *obj;
+ int i;
+ struct ylist_head *lh;
+
+ if (yaffs_SkipVerification(dev))
+ return;
+
+ /* Iterate through the objects in each hash entry */
+
+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
+ ylist_for_each(lh, &dev->objectBucket[i].list) {
+ if (lh) {
+ obj = ylist_entry(lh, yaffs_Object, hashLink);
+ yaffs_VerifyObject(obj);
+ }
+ }
+ }
+}
+
+
+/*
+ * Simple hash function. Needs to have a reasonable spread
+ */
+
+static Y_INLINE int yaffs_HashFunction(int n)
+{
+ n = abs(n);
+ return n % YAFFS_NOBJECT_BUCKETS;
+}
+
+/*
+ * Access functions to useful fake objects.
+ * Note that root might have a presence in NAND if permissions are set.
+ */
+
+yaffs_Object *yaffs_Root(yaffs_Device *dev)
+{
+ return dev->rootDir;
+}
+
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
+{
+ return dev->lostNFoundDir;
+}
+
+
+/*
+ * Erased NAND checking functions
+ */
+
+int yaffs_CheckFF(__u8 *buffer, int nBytes)
+{
+ /* Horrible, slow implementation */
+ while (nBytes--) {
+ if (*buffer != 0xFF)
+ return 0;
+ buffer++;
+ }
+ return 1;
+}
+
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
+ int chunkInNAND)
+{
+ int retval = YAFFS_OK;
+ __u8 *data = yaffs_GetTempBuffer(dev, __LINE__);
+ yaffs_ExtendedTags tags;
+ int result;
+
+ result = yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags);
+
+ if (tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR)
+ retval = YAFFS_FAIL;
+
+ if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) {
+ T(YAFFS_TRACE_NANDACCESS,
+ (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND));
+ retval = YAFFS_FAIL;
+ }
+
+ yaffs_ReleaseTempBuffer(dev, data, __LINE__);
+
+ return retval;
+
+}
+
+
+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,
+ int useReserve)
+{
+ int attempts = 0;
+ int writeOk = 0;
+ int chunk;
+
+ yaffs_InvalidateCheckpoint(dev);
+
+ do {
+ yaffs_BlockInfo *bi = 0;
+ int erasedOk = 0;
+
+ chunk = yaffs_AllocateChunk(dev, useReserve, &bi);
+ if (chunk < 0) {
+ /* no space */
+ break;
+ }
+
+ /* First check this chunk is erased, if it needs
+ * checking. The checking policy (unless forced
+ * always on) is as follows:
+ *
+ * Check the first page we try to write in a block.
+ * If the check passes then we don't need to check any
+ * more. If the check fails, we check again...
+ * If the block has been erased, we don't need to check.
+ *
+ * However, if the block has been prioritised for gc,
+ * then we think there might be something odd about
+ * this block and stop using it.
+ *
+ * Rationale: We should only ever see chunks that have
+ * not been erased if there was a partially written
+ * chunk due to power loss. This checking policy should
+ * catch that case with very few checks and thus save a
+ * lot of checks that are most likely not needed.
+ *
+ * Mods to the above
+ * If an erase check fails or the write fails we skip the
+ * rest of the block.
+ */
+
+ /* let's give it a try */
+ attempts++;
+
+#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED
+ bi->skipErasedCheck = 0;
+#endif
+ if (!bi->skipErasedCheck) {
+ erasedOk = yaffs_CheckChunkErased(dev, chunk);
+ if (erasedOk != YAFFS_OK) {
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("**>> yaffs chunk %d was not erased"
+ TENDSTR), chunk));
+
+ /* If not erased, delete this one,
+ * skip rest of block and
+ * try another chunk */
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
+ yaffs_SkipRestOfBlock(dev);
+ continue;
+ }
+ }
+
+ 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);
+ continue;
+ }
+
+ bi->skipErasedCheck = 1;
+
+ /* Copy the data into the robustification buffer */
+ yaffs_HandleWriteChunkOk(dev, chunk, data, tags);
+
+ } while (writeOk != YAFFS_OK &&
+ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts));
+
+ if (!writeOk)
+ chunk = -1;
+
+ if (attempts > 1) {
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("**>> yaffs write required %d attempts" TENDSTR),
+ attempts));
+
+ dev->nRetriedWrites += (attempts - 1);
+ }
+
+ return chunk;
+}
+
+/*
+ * Block retiring for handling a broken block.
+ */
+
+static void yaffs_RetireBlock(yaffs_Device *dev, int blockInNAND)
+{
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
+
+ yaffs_InvalidateCheckpoint(dev);
+
+ if (yaffs_MarkBlockBad(dev, blockInNAND) != YAFFS_OK) {
+ if (yaffs_EraseBlockInNAND(dev, blockInNAND) != YAFFS_OK) {
+ T(YAFFS_TRACE_ALWAYS, (TSTR(
+ "yaffs: Failed to mark bad and erase block %d"
+ TENDSTR), blockInNAND));
+ } else {
+ yaffs_ExtendedTags tags;
+ int chunkId = blockInNAND * dev->param.nChunksPerBlock;
+
+ __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__);
+
+ memset(buffer, 0xff, dev->nDataBytesPerChunk);
+ yaffs_InitialiseTags(&tags);
+ tags.sequenceNumber = YAFFS_SEQUENCE_BAD_BLOCK;
+ if (dev->param.writeChunkWithTagsToNAND(dev, chunkId -
+ dev->chunkOffset, buffer, &tags) != YAFFS_OK)
+ T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Failed to "
+ TCONT("write bad block marker to block %d")
+ TENDSTR), blockInNAND));
+
+ yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
+ }
+ }
+
+ bi->blockState = YAFFS_BLOCK_STATE_DEAD;
+ bi->gcPrioritise = 0;
+ bi->needsRetiring = 0;
+
+ dev->nRetiredBlocks++;
+}
+
+/*
+ * Functions for robustisizing TODO
+ *
+ */
+
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev, int chunkInNAND,
+ const __u8 *data,
+ const yaffs_ExtendedTags *tags)
+{
+}
+
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev, int chunkInNAND,
+ const yaffs_ExtendedTags *tags)
+{
+}
+
+void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi)
+{
+ if (!bi->gcPrioritise) {
+ bi->gcPrioritise = 1;
+ dev->hasPendingPrioritisedGCs = 1;
+ bi->chunkErrorStrikes++;
+
+ if (bi->chunkErrorStrikes > 3) {
+ bi->needsRetiring = 1; /* Too many stikes, so retire this */
+ T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR)));
+
+ }
+ }
+}
+
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev, int chunkInNAND,
+ int erasedOk)
+{
+ int blockInNAND = chunkInNAND / dev->param.nChunksPerBlock;
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
+
+ yaffs_HandleChunkError(dev, bi);
+
+ if (erasedOk) {
+ /* Was an actual write failure, so mark the block for retirement */
+ bi->needsRetiring = 1;
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ (TSTR("**>> Block %d needs retiring" TENDSTR), blockInNAND));
+ }
+
+ /* Delete the chunk */
+ yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
+ yaffs_SkipRestOfBlock(dev);
+}
+
+
+/*---------------- Name handling functions ------------*/
+
+static __u16 yaffs_CalcNameSum(const YCHAR *name)
+{
+ __u16 sum = 0;
+ __u16 i = 1;
+
+ const YUCHAR *bname = (const YUCHAR *) name;
+ if (bname) {
+ while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH/2))) {
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+ sum += yaffs_toupper(*bname) * i;
+#else