uboot-mt623x/fs/yaffs2/yaffs_guts.c

7492 lines
178 KiB
C
Raw Permalink Normal View History

/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
const char *yaffs_guts_c_version =
"$Id: yaffs_guts.c,v 1.52 2007/10/16 00:45:05 charles Exp $";
#include "yportenv.h"
#include "linux/stat.h"
#include "yaffsinterface.h"
#include "yaffsfs.h"
#include "yaffs_guts.h"
#include "yaffs_tagsvalidity.h"
#include "yaffs_tagscompat.h"
#ifndef CONFIG_YAFFS_USE_OWN_SORT
#include "yaffs_qsort.h"
#endif
#include "yaffs_nand.h"
#include "yaffs_checkptrw.h"
#include "yaffs_nand.h"
#include "yaffs_packedtags2.h"
#include "malloc.h"
#ifdef CONFIG_YAFFS_WINCE
void yfsd_LockYAFFS(BOOL fsLockOnly);
void yfsd_UnlockYAFFS(BOOL fsLockOnly);
#endif
#define YAFFS_PASSIVE_GC_CHUNKS 2
#include "yaffs_ecc.h"
/* Robustification (if it ever comes about...) */
static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND);
static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk);
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);
/* Other local prototypes */
static int yaffs_UnlinkObject( yaffs_Object *obj);
static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj);
static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList);
static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev,
const __u8 * buffer,
yaffs_ExtendedTags * tags,
int useReserve);
static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode,
int chunkInNAND, int inScan);
static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number,
yaffs_ObjectType type);
static void yaffs_AddObjectToDirectory(yaffs_Object * directory,
yaffs_Object * obj);
static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name,
int force, int isShrink, int shadows);
static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj);
static int yaffs_CheckStructures(void);
static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level,
int chunkOffset, int *limit);
static int yaffs_DoGenericObjectDeletion(yaffs_Object * in);
static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo);
static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo);
static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
int lineNo);
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
int chunkInNAND);
static int yaffs_UnlinkWorker(yaffs_Object * obj);
static void yaffs_DestroyObject(yaffs_Object * obj);
static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
int chunkInObject);
loff_t yaffs_GetFileSize(yaffs_Object * obj);
static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr);
static void yaffs_VerifyFreeChunks(yaffs_Device * dev);
static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in);
#ifdef YAFFS_PARANOID
static int yaffs_CheckFileSanity(yaffs_Object * in);
#else
#define yaffs_CheckFileSanity(in)
#endif
static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in);
static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId);
static void yaffs_InvalidateCheckpoint(yaffs_Device *dev);
static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode,
yaffs_ExtendedTags * tags);
static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos);
static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev,
yaffs_FileStructure * fStruct,
__u32 chunkId);
/* Function to calculate chunk and offset */
static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, __u32 *chunk, __u32 *offset)
{
if(dev->chunkShift){
/* Easy-peasy power of 2 case */
*chunk = (__u32)(addr >> dev->chunkShift);
*offset = (__u32)(addr & dev->chunkMask);
}
else if(dev->crumbsPerChunk)
{
/* Case where we're using "crumbs" */
*offset = (__u32)(addr & dev->crumbMask);
addr >>= dev->crumbShift;
*chunk = ((__u32)addr)/dev->crumbsPerChunk;
*offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift);
}
else
YBUG();
}
/* Function to return the number of shifts for a power of 2 greater than or equal
* to the given number
* Note we don't try to cater for all possible numbers and this does not have to
* be hellishly efficient.
*/
static __u32 ShiftsGE(__u32 x)
{
int extraBits;
int nShifts;
nShifts = extraBits = 0;
while(x>1){
if(x & 1) extraBits++;
x>>=1;
nShifts++;
}
if(extraBits)
nShifts++;
return nShifts;
}
/* Function to return the number of shifts to get a 1 in bit 0
*/
static __u32 ShiftDiv(__u32 x)
{
int nShifts;
nShifts = 0;
if(!x) return 0;
while( !(x&1)){
x>>=1;
nShifts++;
}
return nShifts;
}
/*
* Temporary buffer manipulations.
*/
static int yaffs_InitialiseTempBuffers(yaffs_Device *dev)
{
int i;
__u8 *buf = (__u8 *)1;
memset(dev->tempBuffer,0,sizeof(dev->tempBuffer));
for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) {
dev->tempBuffer[i].line = 0; /* not in use */
dev->tempBuffer[i].buffer = buf =
YMALLOC_DMA(dev->nDataBytesPerChunk);
}
return buf ? YAFFS_OK : YAFFS_FAIL;
}
static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo)
{
int i, j;
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
if (dev->tempBuffer[i].line == 0) {
dev->tempBuffer[i].line = lineNo;
if ((i + 1) > dev->maxTemp) {
dev->maxTemp = i + 1;
for (j = 0; j <= i; j++)
dev->tempBuffer[j].maxLine =
dev->tempBuffer[j].line;
}
return dev->tempBuffer[i].buffer;
}
}
T(YAFFS_TRACE_BUFFERS,
(TSTR("Out of temp buffers at line %d, other held by lines:"),
lineNo));
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line));
}
T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR)));
/*
* If we got here then we have to allocate an unmanaged one
* This is not good.
*/
dev->unmanagedTempAllocations++;
return YMALLOC(dev->nDataBytesPerChunk);
}
static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
int lineNo)
{
int i;
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
if (dev->tempBuffer[i].buffer == buffer) {
dev->tempBuffer[i].line = 0;
return;
}
}
if (buffer) {
/* assume it is an unmanaged one. */
T(YAFFS_TRACE_BUFFERS,
(TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR),
lineNo));
YFREE(buffer);
dev->unmanagedTempDeallocations++;
}
}
/*
* Determine if we have a managed buffer.
*/
int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer)
{
int i;
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
if (dev->tempBuffer[i].buffer == buffer)
return 1;
}
for (i = 0; i < dev->nShortOpCaches; i++) {
if( dev->srCache[i].data == buffer )
return 1;
}
if (buffer == dev->checkpointBuffer)
return 1;
T(YAFFS_TRACE_ALWAYS,
(TSTR("yaffs: unmaged buffer detected.\n" TENDSTR)));
return 0;
}
/*
* Chunk bitmap manipulations
*/
static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk)
{
if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) {
T(YAFFS_TRACE_ERROR,
(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),
blk));
YBUG();
}
return dev->chunkBits +
(dev->chunkBitmapStride * (blk - dev->internalStartBlock));
}
static Y_INLINE void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk)
{
if(blk < dev->internalStartBlock || blk > dev->internalEndBlock ||
chunk < 0 || chunk >= dev->nChunksPerBlock) {
T(YAFFS_TRACE_ERROR,
(TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR),blk,chunk));
YBUG();
}
}
static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk)
{
__u8 *blkBits = yaffs_BlockBits(dev, blk);
memset(blkBits, 0, dev->chunkBitmapStride);
}
static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk)
{
__u8 *blkBits = yaffs_BlockBits(dev, blk);
yaffs_VerifyChunkBitId(dev,blk,chunk);
blkBits[chunk / 8] &= ~(1 << (chunk & 7));
}
static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk)
{
__u8 *blkBits = yaffs_BlockBits(dev, blk);
yaffs_VerifyChunkBitId(dev,blk,chunk);
blkBits[chunk / 8] |= (1 << (chunk & 7));
}
static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk)
{
__u8 *blkBits = yaffs_BlockBits(dev, blk);
yaffs_VerifyChunkBitId(dev,blk,chunk);
return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0;
}
static Y_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;
}
static int yaffs_CountChunkBits(yaffs_Device * dev, int blk)
{
__u8 *blkBits = yaffs_BlockBits(dev, blk);
int i;
int n = 0;
for (i = 0; i < dev->chunkBitmapStride; i++) {
__u8 x = *blkBits;
while(x){
if(x & 1)
n++;
x >>=1;
}
blkBits++;
}
return n;
}
/*
* Verification code
*/
static int yaffs_SkipVerification(yaffs_Device *dev)
{
return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
}
static int yaffs_SkipFullVerification(yaffs_Device *dev)
{
return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL));
}
static int yaffs_SkipNANDVerification(yaffs_Device *dev)
{
return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND));
}
static const char * blockStateName[] = {
"Unknown",
"Needs scanning",
"Scanning",
"Empty",
"Allocating",
"Full",
"Dirty",
"Checkpoint",
"Collecting",
"Dead"
};
static void yaffs_VerifyBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n)
{
int actuallyUsed;
int inUse;
if(yaffs_SkipVerification(dev))
return;
/* Report illegal runtime states */
if(bi->blockState <0 || bi->blockState >= YAFFS_NUMBER_OF_BLOCK_STATES)
T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has undefined state %d"TENDSTR),n,bi->blockState));
switch(bi->blockState){
case YAFFS_BLOCK_STATE_UNKNOWN:
case YAFFS_BLOCK_STATE_SCANNING:
case YAFFS_BLOCK_STATE_NEEDS_SCANNING:
T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has bad run-state %s"TENDSTR),
n,blockStateName[bi->blockState]));
}
/* Check pages in use and soft deletions are legal */
actuallyUsed = bi->pagesInUse - bi->softDeletions;
if(bi->pagesInUse < 0 || bi->pagesInUse > dev->nChunksPerBlock ||
bi->softDeletions < 0 || bi->softDeletions > dev->nChunksPerBlock ||
actuallyUsed < 0 || actuallyUsed > dev->nChunksPerBlock)
T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has illegal values pagesInUsed %d softDeletions %d"TENDSTR),
n,bi->pagesInUse,bi->softDeletions));
/* Check chunk bitmap legal */
inUse = yaffs_CountChunkBits(dev,n);
if(inUse != bi->pagesInUse)
T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has inconsistent values pagesInUse %d counted chunk bits %d"TENDSTR),
n,bi->pagesInUse,inUse));
/* Check that the sequence number is valid.
* Ten million is legal, but is very unlikely
*/
if(dev->isYaffs2 &&
(bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || bi->blockState == YAFFS_BLOCK_STATE_FULL) &&
(bi->sequenceNumber < YAFFS_LOWEST_SEQUENCE_NUMBER || bi->sequenceNumber > 10000000 ))
T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has suspect sequence number of %d"TENDSTR),
n,bi->sequenceNumber));
}
static void yaffs_VerifyCollectedBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n)
{
yaffs_VerifyBlock(dev,bi,n);
/* After collection the block should be in the erased state */
/* TODO: This will need to change if we do partial gc */
if(bi->blockState != YAFFS_BLOCK_STATE_EMPTY){
T(YAFFS_TRACE_ERROR,(TSTR("Block %d is in state %d after gc, should be erased"TENDSTR),
n,bi->blockState));
}
}
static void yaffs_VerifyBlocks(yaffs_Device *dev)
{
int i;
int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES];
int nIllegalBlockStates = 0;
if(yaffs_SkipVerification(dev))
return;
memset(nBlocksPerState,0,sizeof(nBlocksPerState));
for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++){
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
yaffs_VerifyBlock(dev,bi,i);
if(bi->blockState >=0 && bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES)
nBlocksPerState[bi->blockState]++;
else
nIllegalBlockStates++;
}
T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR)));
T(YAFFS_TRACE_VERIFY,(TSTR("Block summary"TENDSTR)));
T(YAFFS_TRACE_VERIFY,(TSTR("%d blocks have illegal states"TENDSTR),nIllegalBlockStates));
if(nBlocksPerState[YAFFS_BLOCK_STATE_ALLOCATING] > 1)
T(YAFFS_TRACE_VERIFY,(TSTR("Too many allocating blocks"TENDSTR)));
for(i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++)
T(YAFFS_TRACE_VERIFY,
(TSTR("%s %d blocks"TENDSTR),
blockStateName[i],nBlocksPerState[i]));
if(dev->blocksInCheckpoint != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])
T(YAFFS_TRACE_VERIFY,
(TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR),
dev->blocksInCheckpoint, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]));
if(dev->nErasedBlocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY])
T(YAFFS_TRACE_VERIFY,
(TSTR("Erased block count wrong dev %d count %d"TENDSTR),
dev->nErasedBlocks, nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]));
if(nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING] > 1)
T(YAFFS_TRACE_VERIFY,
(TSTR("Too many collecting blocks %d (max is 1)"TENDSTR),
nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING]));
T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR)));
}
/*
* Verify the object header. oh must be valid, but obj and tags may be NULL in which
* case those tests will not be performed.
*/
static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck)
{
if(yaffs_SkipVerification(obj->myDev))
return;
if(!(tags && obj && oh)){
T(YAFFS_TRACE_VERIFY,
(TSTR("Verifying object header tags %x obj %x oh %x"TENDSTR),
(__u32)tags,(__u32)obj,(__u32)oh));
return;
}
if(oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN ||
oh->type > YAFFS_OBJECT_TYPE_MAX)
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d header type is illegal value 0x%x"TENDSTR),
tags->objectId, oh->type));
if(tags->objectId != obj->objectId)
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d header mismatch objectId %d"TENDSTR),
tags->objectId, obj->objectId));
/*
* Check that the object's parent ids match if parentCheck requested.
*
* Tests do not apply to the root object.
*/
if(parentCheck && tags->objectId > 1 && !obj->parent)
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d header mismatch parentId %d obj->parent is NULL"TENDSTR),
tags->objectId, oh->parentObjectId));
if(parentCheck && obj->parent &&
oh->parentObjectId != obj->parent->objectId &&
(oh->parentObjectId != YAFFS_OBJECTID_UNLINKED ||
obj->parent->objectId != YAFFS_OBJECTID_DELETED))
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d header mismatch parentId %d parentObjectId %d"TENDSTR),
tags->objectId, oh->parentObjectId, obj->parent->objectId));
if(tags->objectId > 1 && oh->name[0] == 0) /* Null name */
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d header name is NULL"TENDSTR),
obj->objectId));
if(tags->objectId > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d header name is 0xFF"TENDSTR),
obj->objectId));
}
static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn,
__u32 level, int chunkOffset)
{
int i;
yaffs_Device *dev = obj->myDev;
int ok = 1;
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) {
int i;
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 && yaffs_SkipVerification(obj->myDev))
return;
dev = obj->myDev;
objectId = obj->objectId;
/* Check file size is consistent with tnode depth */
lastChunk = obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1;
x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS;
requiredTallness = 0;
while (x> 0) {
x >>= YAFFS_TNODES_INTERNAL_BITS;
requiredTallness++;
}
actualTallness = obj->variant.fileVariant.topLevel;
if(requiredTallness > actualTallness )
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR),
obj->objectId,actualTallness, requiredTallness));
/* Check that the chunks in the tnode tree are all correct.
* We do this by scanning through the tnode tree and
* checking the tags for every chunk match.
*/
if(yaffs_SkipNANDVerification(dev))
return;
for(i = 1; i <= lastChunk; i++){
tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant,i);
if (tn) {
__u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i);
if(theChunk > 0){
/* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */
yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags);
if(tags.objectId != objectId || tags.chunkId != i){
T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR),
objectId, i, theChunk,
tags.objectId, tags.chunkId));
}
}
}
}
}
static void yaffs_VerifyDirectory(yaffs_Object *obj)
{
if(obj && yaffs_SkipVerification(obj->myDev))
return;
}
static void yaffs_VerifyHardLink(yaffs_Object *obj)
{
if(obj && yaffs_SkipVerification(obj->myDev))
return;
/* Verify sane equivalent object */
}
static void yaffs_VerifySymlink(yaffs_Object *obj)
{
if(obj && yaffs_SkipVerification(obj->myDev))
return;
/* Verify symlink string */
}
static void yaffs_VerifySpecial(yaffs_Object *obj)
{
if(obj && yaffs_SkipVerification(obj->myDev))
return;
}
static void yaffs_VerifyObject(yaffs_Object *obj)
{
yaffs_Device *dev;
__u32 chunkMin;
__u32 chunkMax;
__u32 chunkIdOk;
__u32 chunkIsLive;
if(!obj)
return;
dev = obj->myDev;
if(yaffs_SkipVerification(dev))
return;
/* Check sane object header chunk */
chunkMin = dev->internalStartBlock * dev->nChunksPerBlock;
chunkMax = (dev->internalEndBlock+1) * dev->nChunksPerBlock - 1;
chunkIdOk = (obj->chunkId >= chunkMin && obj->chunkId <= chunkMax);
chunkIsLive = chunkIdOk &&
yaffs_CheckChunkBit(dev,
obj->chunkId / dev->nChunksPerBlock,
obj->chunkId % dev->nChunksPerBlock);
if(!obj->fake &&
(!chunkIdOk || !chunkIsLive)) {
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d has chunkId %d %s %s"TENDSTR),
obj->objectId,obj->chunkId,
chunkIdOk ? "" : ",out of range",
chunkIsLive || !chunkIdOk ? "" : ",marked as deleted"));
}
if(chunkIdOk && chunkIsLive &&!yaffs_SkipNANDVerification(dev)) {
yaffs_ExtendedTags tags;
yaffs_ObjectHeader *oh;
__u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__);
oh = (yaffs_ObjectHeader *)buffer;
yaffs_ReadChunkWithTagsFromNAND(dev, obj->chunkId,buffer, &tags);
yaffs_VerifyObjectHeader(obj,oh,&tags,1);
yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
}
/* Verify it has a parent */
if(obj && !obj->fake &&
(!obj->parent || obj->parent->myDev != dev)){
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR),
obj->objectId,obj->parent));
}
/* Verify parent is a directory */
if(obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY){
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR),
obj->objectId,obj->parent->variantType));
}
switch(obj->variantType){
case YAFFS_OBJECT_TYPE_FILE:
yaffs_VerifyFile(obj);
break;
case YAFFS_OBJECT_TYPE_SYMLINK:
yaffs_VerifySymlink(obj);
break;
case YAFFS_OBJECT_TYPE_DIRECTORY:
yaffs_VerifyDirectory(obj);
break;
case YAFFS_OBJECT_TYPE_HARDLINK:
yaffs_VerifyHardLink(obj);
break;
case YAFFS_OBJECT_TYPE_SPECIAL:
yaffs_VerifySpecial(obj);
break;
case YAFFS_OBJECT_TYPE_UNKNOWN:
default:
T(YAFFS_TRACE_VERIFY,
(TSTR("Obj %d has illegaltype %d"TENDSTR),
obj->objectId,obj->variantType));
break;
}
}
static void yaffs_VerifyObjects(yaffs_Device *dev)
{
yaffs_Object *obj;
int i;
struct list_head *lh;
if(yaffs_SkipVerification(dev))
return;
/* Iterate through the objects in each hash entry */
for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++){
list_for_each(lh, &dev->objectBucket[i].list) {
if (lh) {
obj = list_entry(lh, yaffs_Object, hashLink);
yaffs_VerifyObject(obj);
}
}
}
}
/*
* Simple hash function. Needs to have a reasonable spread
*/
static Y_INLINE int yaffs_HashFunction(int n)
{
/* XXX U-BOOT XXX */
/*n = abs(n); */
if (n < 0)
n = -n;