/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * yaffs_guts.c The main guts of YAFFS
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
*
* Copyright (C) 2002 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.
*
*/
//yaffs_guts.c
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.2 2004-11-16 02:36:15 charles Exp $";
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.12 2005-07-31 06:54:19 charles Exp $";
#include "yportenv.h"
#include "yaffsinterface.h"
#include "yaffs_guts.h"
+#include "yaffs_tagsvalidity.h"
+
#include "yaffs_tagscompat.h"
#include "yaffs_ecc.h"
#endif
+#if 0
// countBits is a quick way of counting the number of bits in a byte.
// ie. countBits[n] holds the number of 1 bits in a byte with the value n.
return retVal;
}
+#endif
+
#if 0
// Stuff using yea olde tags
static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device *dev,int blockNo, yaffs_BlockState *state,unsigned *sequenceNumber);
// Local prototypes
static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_ExtendedTags *tags, int useReserve);
+#if 0
static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
+#endif
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);
+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 void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo);
-static int yaffs_ValidateTags(yaffs_ExtendedTags *tags);
-
// Robustification (if it ever comes about...)
static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND);
+#if 0
static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND);
+#endif
static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND);
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);
static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
+static void yaffs_VerifyFreeChunks(yaffs_Device *dev);
+
#ifdef YAFFS_PARANOID
static int yaffs_CheckFileSanity(yaffs_Object *in);
#else
static int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *buffer, yaffs_ExtendedTags *tags)
{
+ chunkInNAND -= dev->chunkOffset;
+
if(dev->readChunkWithTagsFromNAND)
return dev->readChunkWithTagsFromNAND(dev,chunkInNAND,buffer,tags);
else
static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_ExtendedTags *tags)
{
+ chunkInNAND -= dev->chunkOffset;
+
if(tags)
{
tags->sequenceNumber = dev->sequenceNumber;
static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device *dev, int blockNo)
{
+ blockNo -= dev->blockOffset;
+
if(dev->markNANDBlockBad)
return dev->markNANDBlockBad(dev,blockNo);
else
}
static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device *dev,int blockNo, yaffs_BlockState *state, unsigned *sequenceNumber)
{
+ blockNo -= dev->blockOffset;
+
if(dev->queryNANDBlock)
return dev->queryNANDBlock(dev,blockNo,state,sequenceNumber);
else
return yaffs_TagsCompatabilityQueryNANDBlock(dev,blockNo,state,sequenceNumber);
}
-int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
+static int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
{
int result;
+
+ blockInNAND -= dev->blockOffset;
dev->nBlockErasures++;
result = dev->eraseBlockInNAND(dev,blockInNAND);
return result;
}
-int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
+static int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
{
return dev->initialiseNAND(dev);
}
static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
{
- if(blk < dev->startBlock || blk > dev->endBlock)
+ 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->startBlock));
+ return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->internalStartBlock));
}
static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
return 0;
}
-#if 0
-// Function to manipulate block info
-static Y_INLINE yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)
-{
- if(blk < dev->startBlock || blk > dev->endBlock)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),blk));
- YBUG();
- }
- return &dev->blockInfo[blk - dev->startBlock];
-}
-#endif
-
static Y_INLINE int yaffs_HashFunction(int n)
{
return dev->lostNFoundDir;
}
-#if 0
-static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
-{
- if(chunkInNAND < dev->startBlock * dev->nChunksPerBlock)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
- return YAFFS_FAIL;
- }
-
- dev->nPageWrites++;
- return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
-}
-
-
-
-static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
- int chunkInNAND,
- __u8 *data,
- yaffs_Spare *spare,
- int doErrorCorrection)
-{
- int retVal;
- yaffs_Spare localSpare;
-
- dev->nPageReads++;
-
-
-
-
- if(!spare && data)
- {
- // If we don't have a real spare, then we use a local one.
- // Need this for the calculation of the ecc
- spare = &localSpare;
- }
-
-
- if(!dev->useNANDECC)
- {
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
- if(data && doErrorCorrection)
- {
- // Do ECC correction
- //Todo handle any errors
- int eccResult1,eccResult2;
- __u8 calcEcc[3];
-
- yaffs_ECCCalculate(data,calcEcc);
- eccResult1 = yaffs_ECCCorrect (data,spare->ecc1, calcEcc);
- yaffs_ECCCalculate(&data[256],calcEcc);
- eccResult2 = yaffs_ECCCorrect(&data[256],spare->ecc2, calcEcc);
-
- if(eccResult1>0)
- {
- T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
- dev->eccFixed++;
- }
- else if(eccResult1<0)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
- dev->eccUnfixed++;
- }
-
- if(eccResult2>0)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
- dev->eccFixed++;
- }
- else if(eccResult2<0)
- {
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
- dev->eccUnfixed++;
- }
-
- if(eccResult1 || eccResult2)
- {
- // Hoosterman, we had a data problem on this page
- yaffs_HandleReadDataError(dev,chunkInNAND);
- }
- }
- }
- else
- {
- // Must allocate enough memory for spare+2*sizeof(int) for ecc results from device.
- struct yaffs_NANDSpare nspare;
- 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.eccres1 || nspare.eccres2)
- {
- // Hoosterman, we had a data problem on this page
- yaffs_HandleReadDataError(dev,chunkInNAND);
- }
-
- }
- }
- return retVal;
-}
-
-#endif
//Horrible, slow implementation
while(nBytes--)
{
- if(*buffer != 0xFF) return 0;
+ if(*buffer != 0xFF) return 0;
+ buffer++;
}
return 1;
}
__u8 *data = yaffs_GetTempBuffer(dev,__LINE__);
yaffs_ExtendedTags tags;
- dev->readChunkWithTagsFromNAND(dev,chunkInNAND,data,&tags);
+// NCB dev->readChunkWithTagsFromNAND(dev,chunkInNAND,data,&tags);
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND,data,&tags);
if(!yaffs_CheckFF(data,dev->nBytesPerChunk) || tags.chunkUsed)
{
}
-
+#if 0
static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)
{
dev->doingBufferedBlockRewrite = 1;
{
}
+#endif
+
static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags)
{
}
return sum;
}
-void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name)
+static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name)
{
#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
if(name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH)
{
newTnodes[i].internal[0] = &newTnodes[i+1];
#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
- newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = 1;
+ newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
#endif
}
newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
- newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = 1;
+ newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
#endif
dev->freeTnodes = newTnodes;
dev->nFreeTnodes+= nTnodes;
{
tn = dev->freeTnodes;
#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
- if(tn->internal[YAFFS_NTNODES_INTERNAL] != 1)
+ if(tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1)
{
// Hoosterman, this thing looks like it isn't in the list
T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 1" TENDSTR)));
// 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;
+ tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
#endif
tn->internal[0] = dev->freeTnodes;
dev->freeTnodes = tn;
}
-int yaffs_FindChunkInGroup(yaffs_Device *dev, int theChunk, yaffs_ExtendedTags *tags, int objectId, int chunkInInode)
+static int yaffs_FindChunkInGroup(yaffs_Device *dev, int theChunk, yaffs_ExtendedTags *tags, int objectId, int chunkInInode)
{
int j;
if(theBlock)
{
theBlock->softDeletions++;
+ dev->nFreeChunks++;
}
}
obj->unlinkAllowed= 0; // ... or unlink it
obj->deleted = 0;
obj->unlinked = 0;
- obj->st_mode = mode;
+ obj->yst_mode = mode;
obj->myDev = dev;
obj->chunkId = 0; // Not a valid chunk.
}
-int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
+static int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
{
static int x = 0;
int i;
return n;
}
-void yaffs_HashObject(yaffs_Object *in)
+static void yaffs_HashObject(yaffs_Object *in)
{
int bucket = yaffs_HashFunction(in->objectId);
yaffs_Device *dev = in->myDev;
in = list_entry(i, yaffs_Object,hashLink);
if(in->objectId == number)
{
+#ifdef __KERNEL__
+ // Don't tell the VFS about this one if it is defered free
+ if(in->deferedFree)
+ return NULL;
+#endif
+
return in;
}
}
#else
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = Y_CURRENT_TIME;
+ theObject->yst_atime = theObject->yst_mtime = theObject->yst_ctime = Y_CURRENT_TIME;
#endif
switch(type)
{
case YAFFS_OBJECT_TYPE_FILE:
theObject->variant.fileVariant.fileSize = 0;
theObject->variant.fileVariant.scannedFileSize = 0;
+ theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; // max __u32
theObject->variant.fileVariant.topLevel = 0;
theObject->variant.fileVariant.top = yaffs_GetTnode(dev);
break;
return theObject;
}
-yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
+static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
{
yaffs_Object *theObject = NULL;
}
-YCHAR *yaffs_CloneString(const YCHAR *str)
+static YCHAR *yaffs_CloneString(const YCHAR *str)
{
YCHAR *newStr = NULL;
// equivalentObject only has meaning for a hard link;
// aliasString only has meaning for a sumlink.
// rdev only has meaning for devices (a subset of special objects)
-yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
+static yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
yaffs_Object *parent,
const YCHAR *name,
__u32 mode,
in->valid = 1;
in->variantType = type;
- in->st_mode = mode;
+ in->yst_mode = mode;
#ifdef CONFIG_YAFFS_WINCE
yfsd_WinFileTimeNow(in->win_atime);
in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
#else
- in->st_atime = in->st_mtime = in->st_ctime = Y_CURRENT_TIME;
+ in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
- in->st_rdev = rdev;
- in->st_uid = uid;
- in->st_gid = gid;
+ in->yst_rdev = rdev;
+ in->yst_uid = uid;
+ in->yst_gid = gid;
#endif
in->nDataChunks = 0;
}
if(/*yaffs_GetNumberOfFreeChunks(dev) <= 0 || */
- yaffs_UpdateObjectHeader(in,name,0,0) < 0)
+ yaffs_UpdateObjectHeader(in,name,0,0,0) < 0)
{
// Could not create the object header, fail the creation
yaffs_DestroyObject(in);
yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev);
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL,parent,name,mode,uid,gid,NULL,NULL,rdev);
}
yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid,const YCHAR *alias)
}
-static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const YCHAR *newName,int force)
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const YCHAR *newName,int force,int shadows)
{
int unlinkOp;
int deleteOp;
+
+ yaffs_Object * existingTarget;
if(newDir == NULL)
{
newDir = obj->parent; // use the old directory
}
+
+ if(newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragendy: yaffs_ChangeObjectName: newDir is not a directory"TENDSTR)));
+ YBUG();
+ }
// TODO: Do we need this different handling for YAFFS2 and YAFFS1??
if(obj->myDev->isYaffs2)
deleteOp = (newDir == obj->myDev->deletedDir);
+ existingTarget = yaffs_FindObjectByName(newDir,newName);
+
// If the object is a file going into the unlinked directory, then it is OK to just stuff it in since
// duplicate names are allowed.
// Otherwise only proceed if the new name does not exist and if we're putting it into a directory.
if( (unlinkOp||
deleteOp ||
force ||
- !yaffs_FindObjectByName(newDir,newName)) &&
+ (shadows > 0) ||
+ !existingTarget) &&
newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
{
yaffs_SetObjectName(obj,newName);
if(unlinkOp) obj->unlinked = 1;
- // If it is a dletion then we mark it as a shrink for gc purposes.
- if(yaffs_UpdateObjectHeader(obj,newName,0,deleteOp) >= 0)
+ // If it is a deletion then we mark it as a shrink for gc purposes.
+ if(yaffs_UpdateObjectHeader(obj,newName,0,deleteOp,shadows) >= 0)
{
return YAFFS_OK;
}
int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, yaffs_Object *newDir, const YCHAR *newName)
{
yaffs_Object *obj;
+ yaffs_Object *existingTarget;
int force = 0;
#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
#endif
obj = yaffs_FindObjectByName(oldDir,oldName);
+
if(obj && obj->renameAllowed)
{
- return yaffs_ChangeObjectName(obj,newDir,newName,force);
+
+ // Now do the handling for an existing target, if there is one
+
+ existingTarget = yaffs_FindObjectByName(newDir,newName);
+ if(existingTarget &&
+ existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
+ !list_empty(&existingTarget->variant.directoryVariant.children))
+ {
+ // There is a target that is a non-empty directory, so we have to fail
+ return YAFFS_FAIL; // EEXIST or ENOTEMPTY
+ }
+ else if(existingTarget)
+ {
+ // Nuke the target first, using shadowing
+ yaffs_ChangeObjectName(obj,newDir,newName,force,existingTarget->objectId);
+ yaffs_Unlink(newDir,newName);
+ }
+
+
+ return yaffs_ChangeObjectName(obj,newDir,newName,force,0);
}
return YAFFS_FAIL;
}
+#if 0
static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)
{
return ok;
}
-#if 0
+
void yaffs_ObjectTest(yaffs_Device *dev)
{
yaffs_Object *in[1000];
{
seq = dev->sequenceNumber;
- for(i = dev->startBlock; i <= dev->endBlock; i++)
+ for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
{
b = yaffs_GetBlockInfo(dev,i);
if(b->blockState == YAFFS_BLOCK_STATE_FULL &&
}
pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+
if(aggressive)
{
- iterations = dev->endBlock - dev->startBlock + 1;
+ iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
}
else
{
- iterations = dev->endBlock - dev->startBlock + 1;
+ iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
iterations = iterations / 16;
if(iterations > 200)
{
for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
{
b++;
- if ( b < dev->startBlock || b > dev->endBlock)
+ if ( b < dev->internalStartBlock || b > dev->internalEndBlock)
{
- b = dev->startBlock;
+ b = dev->internalStartBlock;
}
- if(b < dev->startBlock || b > dev->endBlock)
+ if(b < dev->internalStartBlock || b > dev->internalEndBlock)
{
T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));
YBUG();
}
else
{
+ dev->nFreeChunks -= dev->nChunksPerBlock; // We lost a block of free space
+
yaffs_RetireBlock(dev,blockNo);
T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo));
}
}
+#if 0
static void yaffs_DumpBlockStats(yaffs_Device *dev)
{
int i,j;
yaffs_BlockInfo *bi;
- for(i= dev->startBlock; i <=dev->endBlock; i++)
+ for(i= dev->internalStartBlock; i <=dev->internalEndBlock; i++)
{
bi = yaffs_GetBlockInfo(dev,i);
T(YAFFS_TRACE_ALLOCATE,(TSTR("%3d state %d shrink %d inuse %d/%d seq %d pages"),i,
}
}
+#endif
static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
// Find an empty block.
- for(i = dev->startBlock; i <= dev->endBlock; i++)
+ for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
{
dev->allocationBlockFinder++;
- if(dev->allocationBlockFinder < dev->startBlock || dev->allocationBlockFinder> dev->endBlock)
+ if(dev->allocationBlockFinder < dev->internalStartBlock || dev->allocationBlockFinder> dev->internalEndBlock)
{
- dev->allocationBlockFinder = dev->startBlock;
+ dev->allocationBlockFinder = dev->internalStartBlock;
}
bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
dev->sequenceNumber++;
bi->sequenceNumber = dev->sequenceNumber;
dev->nErasedBlocks--;
- T(YAFFS_TRACE_ALLOCATE,(TSTR("Allocated block %d, seq %d" TENDSTR),dev->allocationBlockFinder,dev->sequenceNumber));
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("Allocated block %d, seq %d, %d left" TENDSTR),dev->allocationBlockFinder,dev->sequenceNumber, dev->nErasedBlocks));
return dev->allocationBlockFinder;
}
}
- T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no more eraased blocks, but there should have been one" TENDSTR)));
+
+
+ T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs tragedy: no more eraased blocks, but there should have been %d" TENDSTR),dev->nErasedBlocks));
+
+
+
return -1;
}
+// To determine if we have enough space we just look at the
+// number of erased blocks.
+
+static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev)
+{
+ int reservedChunks = (dev->nReservedBlocks * dev->nChunksPerBlock);
+ return (dev->nFreeChunks > reservedChunks);
+}
+
static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
{
dev->allocationPage = 0;
}
- if(!useReserve && dev->nErasedBlocks </*=*/ dev->nReservedBlocks)
+ if(!useReserve && !yaffs_CheckSpaceForAllocation(dev))
{
// Not enough space to allocate unless we're allowed to use the reserve.
return -1;
}
-// To determine if we have enough space we just look at the
-// number of erased blocks.
-// The cache is allowed to use reserved blocks.
-
-static int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
-{
- return (dev->nErasedBlocks >= dev->nReservedBlocks);
-}
static int yaffs_GetErasedChunks(yaffs_Device *dev)
}
-int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
+static int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
{
int oldChunk;
int newChunk;
T(YAFFS_TRACE_TRACING,(TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR),block,bi->pagesInUse,bi->hasShrinkHeader));
//T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));
+
+ //yaffs_VerifyFreeChunks(dev);
bi->hasShrinkHeader = 0; // clear the flag so that the block can erase
+
+ dev->nFreeChunks -= bi->softDeletions; // Take off the number of soft deleted entries because
+ // they're going to get really deleted during GC.
+ dev->isDoingGC = 1;
if(!yaffs_StillSomeChunkBits(dev,block))
{
else
{
- __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__);
+ __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__);
for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock;
chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
if(object && object->deleted && tags.chunkId != 0)
{
// Data chunk in a deleted file, throw it away
- // It's a deleted data chunk,
+ // It's a soft deleted data chunk,
// No need to copy this, just forget about it and fix up the
// object.
yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
+ //yaffs_VerifyFreeChunks(dev);
// Do any required cleanups
for(i = 0; i < cleanups; i++)
{
T(YAFFS_TRACE_GC,(TSTR("gc did not increase free chunks before %d after %d" TENDSTR),chunksBefore,chunksAfter));
}
+
+
+ dev->isDoingGC = 0;
+
+ //yaffs_VerifyFreeChunks(dev);
return YAFFS_OK;
}
#endif
-#if 0
-#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
-static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
-{
- int block;
- int aggressive=0;
-
- //yaffs_DoUnlinkedFileDeletion(dev);
-
- if(dev->nErasedBlocks <= (dev->nReservedBlocks + YAFFS_GARBAGE_COLLECT_LOW_WATER))
- {
- aggressive = 1;
- }
-
- if(aggressive)
- {
- block = yaffs_FindBlockForGarbageCollection(dev,aggressive);
-
- if(block >= 0)
- {
- dev->garbageCollections++;
- return yaffs_GarbageCollectBlock(dev,block);
- }
- else
- {
- return YAFFS_FAIL;
- }
- }
- return YAFFS_OK;
-}
-#endif
// New garbage collector
// If we're very low on erased blocks then we do aggressive garbage collection
// The idea is to help clear out space in a more spread-out manner.
// Dunno if it really does anything useful.
//
-int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
{
int block;
int aggressive;
int gcOk = YAFFS_OK;
int maxTries = 0;
- //yaffs_DoUnlinkedFileDeletion(dev);
+ //yaffs_VerifyFreeChunks(dev);
+
+ if(dev->isDoingGC)
+ {
+ // Bail out so we don't get recursive gc
+ return YAFFS_OK;
+ }
// This loop should pass the first time.
// We'll only see looping here if the erase of the collected block fails.
do{
maxTries++;
- if(dev->nErasedBlocks <= (dev->nReservedBlocks + 2))
+ if(dev->nErasedBlocks < dev->nReservedBlocks)
{
// We need a block soon...
aggressive = 1;
gcOk = yaffs_GarbageCollectBlock(dev,block);
}
- if(dev->nErasedBlocks <= (dev->nReservedBlocks + 1))
+ if(dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0)
{
T(YAFFS_TRACE_GC,(TSTR("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" TENDSTR),dev->nErasedBlocks,maxTries,block));
}
- } while((dev->nErasedBlocks <= (dev->nReservedBlocks + 1)) && (block > 0) && (maxTries < 5));
+ } while((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0) && (maxTries < 2));
return aggressive ? gcOk: YAFFS_OK;
}
//////////////////////////// TAGS ///////////////////////////////////////
-void yaffs_InitialiseTags(yaffs_ExtendedTags *tags)
-{
- memset(tags,0,sizeof(yaffs_ExtendedTags));
- tags->validMarker0 = 0xAAAAAAAA;
- tags->validMarker1 = 0x55555555;
-}
-
-static int yaffs_ValidateTags(yaffs_ExtendedTags *tags)
-{
- return (tags->validMarker0 == 0xAAAAAAAA && tags->validMarker1 == 0x55555555);
-
-}
-
#if 0
}
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
-int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
+static int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
{
//Get the Tnode, then get the level 0 offset chunk offset
yaffs_Tnode *tn;
return retVal;
}
-int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
+static int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
{
//Get the Tnode, then get the level 0 offset chunk offset
yaffs_Tnode *tn;
static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
{
+ // NB inScan is zero unless scanning. For forward scanning, inScan is > 0; for backward scanning inScan is < 0
yaffs_Tnode *tn;
yaffs_Device *dev = in->myDev;
int existingChunk;
yaffs_ExtendedTags existingTags;
yaffs_ExtendedTags newTags;
unsigned existingSerial, newSerial;
+
+ if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
+ {
+ // Just ignore an attempt at putting a chunk into a non-file during scanning
+ // If it is not during Scanning then something went wrong!
+ if(!inScan)
+ {
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy:attempt to put data chunk into a non-file" TENDSTR)));
+ YBUG();
+ }
+
+ yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
+ return YAFFS_OK;
+ }
tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
if(!tn)
existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];
- if(inScan)
+ if(inScan != 0)
{
// If we're scanning then we need to test for duplicates
// NB This does not need to be efficient since it should only ever
// happen when the power fails during a write, then only one
// chunk should ever be affected.
+ //
+ // Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
+ // Update: For backward scanning we don't need to re-read tags so this is quite cheap.
+
if(existingChunk != 0)
// NB Right now existing chunk will not be real chunkId if the device >= 32MB
// thus we have to do a FindChunkInFile to get the real chunk id.
//
- // We have a duplicate now we need to decide which one to use
- // To do this we get both sets of tags and compare serial numbers.
- yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND, NULL,&newTags);
+ // We have a duplicate now we need to decide which one to use:
+ //
+ // Backwards scanning YAFFS2: The old one is what we use, dump the new one.
+ // Forward scanning YAFFS2: The new one is what we use, dump the old one.
+ // YAFFS1: Get both sets of tags and compare serial numbers.
+ //
+ //
+
+ if(inScan > 0)
+ {
+ // Only do this for forward scanning
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND, NULL,&newTags);
- // Do a proper find
- existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);
+ // Do a proper find
+ existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);
+ }
if(existingChunk <=0)
{
newSerial = newTags.serialNumber;
existingSerial = existingTags.serialNumber;
- if( in->myDev->isYaffs2 ||
- existingChunk <= 0 ||
- ((existingSerial+1) & 3) == newSerial)
+ if( (inScan > 0) &&
+ ( in->myDev->isYaffs2 ||
+ existingChunk <= 0 ||
+ ((existingSerial+1) & 3) == newSerial))
{
+ // Forward scanning.
// Use new
// Delete the old one and drop through to update the tnode
yaffs_DeleteChunk(dev,existingChunk,1,__LINE__);
}
else
{
+ // Backward scanning or we want to use the existing one
// Use existing.
// Delete the new one and return early so that the tnode isn't changed
yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
-int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
+static int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
{
int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
bi->blockState == YAFFS_BLOCK_STATE_COLLECTING)
{
- dev->nFreeChunks++;
+ dev->nFreeChunks++;
yaffs_ClearChunkBit(dev,block,page);
+
bi->pagesInUse--;
if(bi->pagesInUse == 0 &&
-int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)
+static int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)
{
// Find old chunk Need to do this to get serial number
// Write new one and patch into tree.
// UpdateObjectHeader updates the header on NAND for an object.
// If name is not NULL, then that new name is used.
//
-int yaffs_UpdateObjectHeader(yaffs_Object *in,const YCHAR *name, int force,int isShrink)
+int yaffs_UpdateObjectHeader(yaffs_Object *in,const YCHAR *name, int force,int isShrink,int shadows)
{
yaffs_BlockInfo *bi;
// Header data
oh->type = in->variantType;
- oh->st_mode = in->st_mode;
+ oh->yst_mode = in->yst_mode;
+
+ // shadowing
+ oh->shadowsObject = shadows;
#ifdef CONFIG_YAFFS_WINCE
oh->win_atime[0] = in->win_atime[0];
oh->win_ctime[1] = in->win_ctime[1];
oh->win_mtime[1] = in->win_mtime[1];
#else
- oh->st_uid = in->st_uid;
- oh->st_gid = in->st_gid;
- oh->st_atime = in->st_atime;
- oh->st_mtime = in->st_mtime;
- oh->st_ctime = in->st_ctime;
- oh->st_rdev = in->st_rdev;
+ oh->yst_uid = in->yst_uid;
+ oh->yst_gid = in->yst_gid;
+ oh->yst_atime = in->yst_atime;
+ oh->yst_mtime = in->yst_mtime;
+ oh->yst_ctime = in->yst_ctime;
+ oh->yst_rdev = in->yst_rdev;
#endif
if(in->parent)
{
// Should not happen
break;
case YAFFS_OBJECT_TYPE_FILE:
- oh->fileSize = in->variant.fileVariant.fileSize;
+ oh->fileSize = (oh->parentObjectId == YAFFS_OBJECTID_DELETED ||
+ oh->parentObjectId == YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant.fileVariant.fileSize;
break;
case YAFFS_OBJECT_TYPE_HARDLINK:
oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
newTags.chunkId = 0;
newTags.objectId = in->objectId;
newTags.serialNumber = in->serial;
-
-
+
+ // Add extra info for file header
+
+ newTags.extraHeaderInfoAvailable = 1;
+ newTags.extraParentObjectId = oh->parentObjectId;
+ newTags.extraFileLength = oh->fileSize;
+ newTags.extraIsShrinkHeader = oh->isShrink;
+ newTags.extraEquivalentObjectId = oh->equivalentObjectId;
+ newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0;
+ newTags.extraObjectType = in->variantType;
+
// Create new chunk in NAND
newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags, (prevChunkId >= 0) ? 1 : 0 );
static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
{
yaffs_Device *dev = obj->myDev;
- int lowest;
+ int lowest = -99; // Stop compiler whining.
int i;
yaffs_ChunkCache *cache;
- int chunkWritten;
+ int chunkWritten = 0;
//int nBytes;
int nCaches = obj->myDev->nShortOpCaches;
// its cache, then find again.
// NB what's here is not very accurate, we actually flush the object
// the last recently used page.
+
+ // With locking we can't assume we can use entry zero
+
theObj = NULL;
- usage = 0;
+ usage = -1;
cache = NULL;
- pushout = 0;
+ pushout = -1;
for(i = 0; i < dev->nShortOpCaches; i++)
{
}
}
-
return cache;
}
else
-int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)
+int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes, int writeThrough)
{
int chunk;
// OK now check for the curveball where the start and end are in
// the same chunk.
- if( (start + n) < dev->nBytesPerChunk)
+ if((start + n) < dev->nBytesPerChunk)
{
nToCopy = n;
{
// An incomplete start or end chunk (or maybe both start and end chunk)
if(dev->nShortOpCaches > 0)
- {
+ {
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(!cache && yaffs_CheckSpaceForAllocation(in->myDev))
{
cache = yaffs_GrabChunkCache(in->myDev);
cache->object = in;
#endif
cache->locked = 0;
cache->nBytes = nToWriteBack;
+
+ if(writeThrough)
+ {
+ chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
+ cache->chunkId,
+ cache->data,
+ cache->nBytes,1);
+ cache->dirty = 0;
+ }
+
}
else
{
chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
if(chunkId > 0)
{
- if(chunkId < (dev->startBlock * dev->nChunksPerBlock) ||
- chunkId >= ((dev->endBlock+1) * dev->nChunksPerBlock))
+ if(chunkId < (dev->internalStartBlock * dev->nChunksPerBlock) ||
+ chunkId >= ((dev->internalEndBlock+1) * dev->nChunksPerBlock))
{
- T(YAFFS_TRACE_ALWAYS,("Found daft chunkId %d for %d\n",chunkId,i));
+ T(YAFFS_TRACE_ALWAYS,(TSTR("Found daft chunkId %d for %d"TENDSTR),chunkId,i));
}
else
{
yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
- // TODO write a new object header to show we've shrunk the file
- // Do this only if the file is not in the deleted directory.
- if(in->parent->objectId != YAFFS_OBJECTID_UNLINKED)
+ // Write a new object header to show we've shrunk the file
+ // Do this only if the file is not in the deleted directories.
+ if(in->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
+ in->parent->objectId != YAFFS_OBJECTID_DELETED
+ )
{
- yaffs_UpdateObjectHeader(in,NULL, 0, 1);
+ yaffs_UpdateObjectHeader(in,NULL, 0, 1,0);
}
yfsd_WinFileTimeNow(in->win_mtime);
#else
- in->st_mtime = Y_CURRENT_TIME;
+ in->yst_mtime = Y_CURRENT_TIME;
#endif
}
- retVal = (yaffs_UpdateObjectHeader(in,NULL,0,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
+ retVal = (yaffs_UpdateObjectHeader(in,NULL,0,0,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
}
else
{
if(in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir))
{
// Move to the unlinked directory so we have a record that it was deleted.
- yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0);
+ yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0,0);
}
yaffs_RemoveObjectFromDirectory(in);
yaffs_DeleteChunk(in->myDev,in->chunkId,1,__LINE__);
in->chunkId = -1;
-
+#if 0
#ifdef __KERNEL__
if(in->myInode)
{
in->myInode = 0;
}
#endif
+#endif
+
yaffs_FreeObject(in);
return YAFFS_OK;
#endif
if(immediateDeletion)
{
- retVal = yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0);
+ retVal = yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0,0);
T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
in->deleted=1;
in->myDev->nDeletedFiles++;
+ if( 0 && in->myDev->isYaffs2)
+ {
+ yaffs_ResizeFile(in,0);
+ }
yaffs_SoftDeleteFile(in);
}
else
{
- retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);
+ retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0,0);
}
}
yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
- retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0);
+ retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0,0);
if(retVal == YAFFS_OK)
{
case YAFFS_OBJECT_TYPE_SYMLINK:
return yaffs_DeleteSymLink(obj);
break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ return yaffs_DoGenericObjectDeletion(obj);
+ break;
case YAFFS_OBJECT_TYPE_HARDLINK:
case YAFFS_OBJECT_TYPE_UNKNOWN:
default:
//////////////// Initialisation Scanning /////////////////
+void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, int backwardScanning)
+{
+ //Todo
+}
+
#if 0
// For now we use the SmartMedia check.
// We look at the blockStatus byte in the first two chunks
yaffs_ObjectHeader *oh;
yaffs_Object *in;
yaffs_Object *parent;
- int nBlocks = dev->endBlock - dev->startBlock + 1;
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
__u8 *chunkData;
yaffs_BlockIndex *blockIndex = NULL;
+
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_Scan starts intstartblk %d intendblk %d..." TENDSTR),dev->internalStartBlock,dev->internalEndBlock));
chunkData = yaffs_GetTempBuffer(dev,__LINE__);
// Scan all the blocks to determine their state
- for(blk = dev->startBlock; blk <= dev->endBlock; blk++)
+ for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
{
bi = yaffs_GetBlockInfo(dev,blk);
yaffs_ClearChunkBits(dev,blk);
bi->blockState = state;
bi->sequenceNumber = sequenceNumber;
+
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block scanning block %d state %d seq %d" TENDSTR),blk,state,sequenceNumber));
if(state == YAFFS_BLOCK_STATE_DEAD)
{
}
else if(state == YAFFS_BLOCK_STATE_EMPTY)
{
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block empty " TENDSTR)));
dev->nErasedBlocks++;
dev->nFreeChunks += dev->nChunksPerBlock;
}
{
startIterator = 0;
endIterator = nBlocksToScan-1;
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("%d blocks to be scanned" TENDSTR),nBlocksToScan));
}
else
{
- startIterator = dev->startBlock;
- endIterator = dev->endBlock;
+ startIterator = dev->internalStartBlock;
+ endIterator = dev->internalEndBlock;
}
+ // For each block....
for(blockIterator = startIterator; blockIterator <= endIterator; blockIterator++)
{
deleted = 0;
+ // For each chunk in each block that needs scanning....
for(c = 0; c < dev->nChunksPerBlock &&
state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++)
{
if(!dev->isYaffs2 && tags.chunkDeleted)
{
+ // YAFFS1 only...
// A deleted chunk
deleted++;
dev->nFreeChunks ++;
}
else if(tags.chunkId > 0)
{
- int endpos;
- // A data chunk.
+ // chunkId > 0 so it is a data chunk...
+ unsigned int endpos;
+
yaffs_SetChunkBit(dev,blk,c);
bi->pagesInUse++;
in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
- // PutChunkIntoFIle checks for a clash (two data chunks with
+ // PutChunkIntoFile checks for a clash (two data chunks with
// the same chunkId).
yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
endpos = (tags.chunkId - 1)* dev->nBytesPerChunk + tags.byteCount;
- if(in->variant.fileVariant.scannedFileSize <endpos)
+ if(in->variantType == YAFFS_OBJECT_TYPE_FILE && in->variant.fileVariant.scannedFileSize <endpos)
{
in->variant.fileVariant.scannedFileSize = endpos;
if(!dev->useHeaderFileSize)
- {
- in->variant.fileVariant.fileSize =
- in->variant.fileVariant.scannedFileSize;
+ {
+ in->variant.fileVariant.fileSize = in->variant.fileVariant.scannedFileSize;
}
}
oh = (yaffs_ObjectHeader *)chunkData;
+ in = yaffs_FindObjectByNumber(dev,tags.objectId);
+ if(in && in->variantType != oh->type)
+ {
+ // This should not happen, but somehow
+ // Wev'e ended up with an objectId that has been reused but not yet
+ // deleted, and worse still it has changed type. Delete the old object.
+
+ yaffs_DestroyObject(in);
+
+ in = 0;
+ }
+
in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
+ if(oh->shadowsObject > 0)
+ {
+ yaffs_HandleShadowedObject(dev,oh->shadowsObject,0);
+ }
+
if(in->valid)
{
// We have already filled this one. We have a duplicate and need to resolve it.
in->valid = 1;
in->variantType = oh->type;
- in->st_mode = oh->st_mode;
+ in->yst_mode = oh->yst_mode;
#ifdef CONFIG_YAFFS_WINCE
in->win_atime[0] = oh->win_atime[0];
in->win_ctime[0] = oh->win_ctime[0];
in->win_ctime[1] = oh->win_ctime[1];
in->win_mtime[1] = oh->win_mtime[1];
#else
- in->st_uid = oh->st_uid;
- in->st_gid = oh->st_gid;
- in->st_atime = oh->st_atime;
- in->st_mtime = oh->st_mtime;
- in->st_ctime = oh->st_ctime;
- in->st_rdev = oh->st_rdev;
+ in->yst_uid = oh->yst_uid;
+ in->yst_gid = oh->yst_gid;
+ in->yst_atime = oh->yst_atime;
+ in->yst_mtime = oh->yst_mtime;
+ in->yst_ctime = oh->yst_ctime;
+ in->yst_rdev = oh->yst_rdev;
#endif
in->chunkId = chunk;
in->valid = 1;
in->variantType = oh->type;
- in->st_mode = oh->st_mode;
+ in->yst_mode = oh->yst_mode;
#ifdef CONFIG_YAFFS_WINCE
in->win_atime[0] = oh->win_atime[0];
in->win_ctime[0] = oh->win_ctime[0];
in->win_ctime[1] = oh->win_ctime[1];
in->win_mtime[1] = oh->win_mtime[1];
#else
- in->st_uid = oh->st_uid;
- in->st_gid = oh->st_gid;
- in->st_atime = oh->st_atime;
- in->st_mtime = oh->st_mtime;
- in->st_ctime = oh->st_ctime;
- in->st_rdev = oh->st_rdev;
+ in->yst_uid = oh->yst_uid;
+ in->yst_gid = oh->yst_gid;
+ in->yst_atime = oh->yst_atime;
+ in->yst_mtime = oh->yst_mtime;
+ in->yst_ctime = oh->yst_ctime;
+ in->yst_rdev = oh->yst_rdev;
#endif
in->chunkId = chunk;
}
if(dev->useHeaderFileSize)
+
in->variant.fileVariant.fileSize = oh->fileSize;
break;
}
+ // Handle the unlinked files. Since they were left in an unlinked state we should
+ // just delete them.
{
struct list_head *i;
struct list_head *n;
if(i)
{
l = list_entry(i, yaffs_Object,siblings);
- if(l->deleted)
- {
- yaffs_DestroyObject(l);
- }
+ yaffs_DestroyObject(l);
}
}
}
yaffs_ReleaseTempBuffer(dev,chunkData,__LINE__);
+
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_Scan ends" TENDSTR)));
+
return YAFFS_OK;
}
+static int yaffs_ScanBackwards(yaffs_Device *dev)
+{
+ yaffs_ExtendedTags tags;
+ int blk;
+ int blockIterator;
+ int startIterator;
+ int endIterator;
+ int nBlocksToScan = 0;
+
+ int chunk;
+ int c;
+ int deleted;
+ yaffs_BlockState state;
+ yaffs_Object *hardList = NULL;
+ yaffs_Object *hl;
+ yaffs_BlockInfo *bi;
+ int sequenceNumber;
+ yaffs_ObjectHeader *oh;
+ yaffs_Object *in;
+ yaffs_Object *parent;
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+
+ __u8 *chunkData;
-////////////////////////// Directory Functions /////////////////////////
-
+ yaffs_BlockIndex *blockIndex = NULL;
-static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
-{
- if(obj->siblings.prev == NULL)
+ if(!dev->isYaffs2)
{
- // Not initialised
- INIT_LIST_HEAD(&obj->siblings);
-
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR)));
+ return YAFFS_FAIL;
+ }
+
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards starts intstartblk %d intendblk %d..." TENDSTR),dev->internalStartBlock,dev->internalEndBlock));
+
+ chunkData = yaffs_GetTempBuffer(dev,__LINE__);
+
+
+ dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
+
+ blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
+
+
+ // Scan all the blocks to determine their state
+ for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
+ {
+ bi = yaffs_GetBlockInfo(dev,blk);
+ yaffs_ClearChunkBits(dev,blk);
+ bi->pagesInUse = 0;
+ bi->softDeletions = 0;
+
+ yaffs_QueryInitialBlockState(dev,blk,&state,&sequenceNumber);
+
+ bi->blockState = state;
+ bi->sequenceNumber = sequenceNumber;
+
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block scanning block %d state %d seq %d" TENDSTR),blk,state,sequenceNumber));
+
+ if(state == YAFFS_BLOCK_STATE_DEAD)
+ {
+ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
+ }
+ else if(state == YAFFS_BLOCK_STATE_EMPTY)
+ {
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block empty " TENDSTR)));
+ dev->nErasedBlocks++;
+ dev->nFreeChunks += dev->nChunksPerBlock;
+ }
+ else if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+
+ // Determine the highest sequence number
+ if( dev->isYaffs2 &&
+ sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
+ sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER)
+ {
+
+ blockIndex[nBlocksToScan].seq = sequenceNumber;
+ blockIndex[nBlocksToScan].block = blk;
+
+ nBlocksToScan++;
+
+ if(sequenceNumber >= dev->sequenceNumber)
+ {
+ dev->sequenceNumber = sequenceNumber;
+ }
+ }
+ else if(dev->isYaffs2)
+ {
+ // TODO: Nasty sequence number!
+ T(YAFFS_TRACE_SCAN,(TSTR("Block scanning block %d has bad sequence number %d" TENDSTR),blk,sequenceNumber));
+
+ }
+ }
+ }
+
+ // Sort the blocks
+ // Dungy old bubble sort for now...
+ {
+ yaffs_BlockIndex temp;
+ int i;
+ int j;
+
+ for(i = 0; i < nBlocksToScan; i++)
+ for(j = i+1; j < nBlocksToScan; j++)
+ if(blockIndex[i].seq > blockIndex[j].seq)
+ {
+ temp = blockIndex[j];
+ blockIndex[j] = blockIndex[i];
+ blockIndex[i] = temp;
+ }
+ }
+
+
+ // Now scan the blocks looking at the data.
+ startIterator = 0;
+ endIterator = nBlocksToScan-1;
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("%d blocks to be scanned" TENDSTR),nBlocksToScan));
+
+
+ // For each block.... backwards
+ for(blockIterator = endIterator; blockIterator >= startIterator; blockIterator--)
+ {
+
+ // get the block to scan in the correct order
+ blk = blockIndex[blockIterator].block;
+
+
+ bi = yaffs_GetBlockInfo(dev,blk);
+ state = bi->blockState;
+
+ deleted = 0;
+
+ if( 0 && // Disable since this is redundant.
+ state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+ // Let's look at the first chunk in the block
+ chunk = blk * dev->nChunksPerBlock;
+
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
+
+ // Let's have a good look at this chunk...
+
+ if(!tags.chunkUsed)
+ {
+ // An unassigned chunk in the block
+ // This means that either the block is empty or
+ // this is the one being allocated from
+
+ // We're looking at the first chunk in the block so the block is unused
+ state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->nErasedBlocks++;
+ dev->nFreeChunks += dev->nChunksPerBlock;
+ }
+
+ }
+
+ // For each chunk in each block that needs scanning....
+ for(c = dev->nChunksPerBlock-1; c >= 0 &&
+ (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
+ state == YAFFS_BLOCK_STATE_ALLOCATING); c--)
+ {
+ // Scan backwards...
+ // Read the tags and decide what to do
+ chunk = blk * dev->nChunksPerBlock + c;
+
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
+
+ // Let's have a good look at this chunk...
+
+ if(!tags.chunkUsed)
+ {
+ // An unassigned chunk in the block
+ // This means that either the block is empty or
+ // this is the one being allocated from
+
+ if(c == 0)
+ {
+ // We're looking at the first chunk in the block so the block is unused
+ state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->nErasedBlocks++;
+ }
+ else
+ {
+ // this is the block being allocated from
+ if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+ T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
+ }
+ state = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->allocationBlock = blk;
+ dev->allocationPage = c;
+ dev->allocationBlockFinder = blk; // Set it to here to encourage the allocator to
+ // go forth from here.
+ //Yaffs2 sanity check:
+ // This should be the one with the highest sequence number
+ if(dev->isYaffs2 && (dev->sequenceNumber != bi->sequenceNumber))
+ {
+ T(YAFFS_TRACE_ALWAYS,
+ (TSTR("yaffs: Allocation block %d was not highest sequence id: block seq = %d, dev seq = %d" TENDSTR),
+ blk,bi->sequenceNumber,dev->sequenceNumber));
+ }
+ }
+
+ dev->nFreeChunks ++;
+ }
+ else if(tags.chunkId > 0)
+ {
+ // chunkId > 0 so it is a data chunk...
+ unsigned int endpos;
+
+ __u32 chunkBase = (tags.chunkId - 1)* dev->nBytesPerChunk;
+
+ yaffs_SetChunkBit(dev,blk,c);
+ bi->pagesInUse++;
+
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
+ if(in->variantType == YAFFS_OBJECT_TYPE_FILE &&
+ chunkBase < in->variant.fileVariant.shrinkSize)
+ {
+ // This has not been invalidated by a resize
+ yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,-1);
+
+
+ // File size is calculated by looking at the data chunks if we have not
+ // seen an object header yet. Stop this practice once we find an object header.
+ endpos = (tags.chunkId - 1)* dev->nBytesPerChunk + tags.byteCount;
+ if(!in->valid && // have not got an object header yet
+ in->variant.fileVariant.scannedFileSize <endpos)
+ {
+ in->variant.fileVariant.scannedFileSize = endpos;
+ in->variant.fileVariant.fileSize = in->variant.fileVariant.scannedFileSize;
+ }
+
+ }
+ else
+ {
+ // This chunk has been invalidated by a resize, so delete
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
+
+
+ }
+ //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));
+ }
+ else
+ {
+ // chunkId == 0, so it is an ObjectHeader.
+ // Thus, we read in the object header and make the object
+ yaffs_SetChunkBit(dev,blk,c);
+ bi->pagesInUse++;
+
+ oh = NULL;
+ in = NULL;
+
+ if(tags.extraHeaderInfoAvailable)
+ {
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,tags.extraObjectType);
+ }
+
+
+ if(!in || !in->valid)
+ {
+
+ // If we don't have valid info then we need to read the chunk
+ // TODO In future we can probably defer reading the chunk and
+ // living with invalid data until needed.
+
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,chunkData,NULL);
+
+ oh = (yaffs_ObjectHeader *)chunkData;
+
+ if(!in)
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
+
+ }
+
+ if(!in)
+ {
+ // TODO Hoosterman we have a problem!
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: Could not make object for object %d at chunk %d during scan" TENDSTR),tags.objectId,chunk));
+
+ }
+
+ if(in->valid)
+ {
+ // We have already filled this one. We have a duplicate that will be discarded, but
+ // we first have to suck out resize info if it is a file.
+
+ if( (in->variantType == YAFFS_OBJECT_TYPE_FILE) &&
+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
+ (tags.extraHeaderInfoAvailable && tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE))
+ )
+ {
+ __u32 thisSize = (oh) ? oh->fileSize : tags.extraFileLength;
+ __u32 parentObjectId = (oh) ? oh->parentObjectId : tags.extraParentObjectId;
+ unsigned isShrink = (oh) ? oh->isShrink : tags.extraIsShrinkHeader;
+
+ // If it is deleted (unlinked at start also means deleted)
+ // we treat the file size as being zeroed at this point.
+ if(parentObjectId == YAFFS_OBJECTID_DELETED ||
+ parentObjectId == YAFFS_OBJECTID_UNLINKED)
+ {
+ thisSize = 0;
+ isShrink = 1;
+ }
+
+ if(in->variant.fileVariant.shrinkSize > thisSize)
+ {
+ in->variant.fileVariant.shrinkSize = thisSize;
+ }
+
+ if(isShrink)
+ {
+ bi->hasShrinkHeader = 1;
+ }
+
+ }
+ // Use existing - destroy this one.
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
+
+ }
+
+ if(!in->valid &&
+ (tags.objectId == YAFFS_OBJECTID_ROOT ||
+ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
+ {
+ // We only load some info, don't fiddle with directory structure
+ in->valid = 1;
+ in->variantType = oh->type;
+
+ in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+ in->win_atime[0] = oh->win_atime[0];
+ in->win_ctime[0] = oh->win_ctime[0];
+ in->win_mtime[0] = oh->win_mtime[0];
+ in->win_atime[1] = oh->win_atime[1];
+ in->win_ctime[1] = oh->win_ctime[1];
+ in->win_mtime[1] = oh->win_mtime[1];
+#else
+ in->yst_uid = oh->yst_uid;
+ in->yst_gid = oh->yst_gid;
+ in->yst_atime = oh->yst_atime;
+ in->yst_mtime = oh->yst_mtime;
+ in->yst_ctime = oh->yst_ctime;
+ in->yst_rdev = oh->yst_rdev;
+#endif
+ in->chunkId = chunk;
+
+ }
+ else if(!in->valid)
+ {
+ // we need to load this info
+
+ in->valid = 1;
+ in->variantType = oh->type;
+
+ in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+ in->win_atime[0] = oh->win_atime[0];
+ in->win_ctime[0] = oh->win_ctime[0];
+ in->win_mtime[0] = oh->win_mtime[0];
+ in->win_atime[1] = oh->win_atime[1];
+ in->win_ctime[1] = oh->win_ctime[1];
+ in->win_mtime[1] = oh->win_mtime[1];
+#else
+ in->yst_uid = oh->yst_uid;
+ in->yst_gid = oh->yst_gid;
+ in->yst_atime = oh->yst_atime;
+ in->yst_mtime = oh->yst_mtime;
+ in->yst_ctime = oh->yst_ctime;
+ in->yst_rdev = oh->yst_rdev;
+#endif
+ in->chunkId = chunk;
+
+ if(oh->shadowsObject > 0)
+ {
+ yaffs_HandleShadowedObject(dev,oh->shadowsObject,0);
+ }
+
+
+ yaffs_SetObjectName(in,oh->name);
+ in->dirty = 0;
+
+ // directory stuff...
+ // hook up to parent
+
+ parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);
+ if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)
+ {
+ // Set up as a directory
+ parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;
+ INIT_LIST_HEAD(&parent->variant.directoryVariant.children);
+ }
+ else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ // Hoosterman, another problem....
+ // We're trying to use a non-directory as a directory
+
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
+ parent = dev->lostNFoundDir;
+ }
+
+ yaffs_AddObjectToDirectory(parent,in);
+
+ if((parent == dev->deletedDir ||
+ parent == dev->unlinkedDir))
+ {
+ in->deleted = 1; // If it is unlinked at start up then it wants deleting
+ }
+
+ if( oh->isShrink)
+ {
+ // Mark the block as having a shrinkHeader
+ bi->hasShrinkHeader = 1;
+ }
+
+
+ // Note re hardlinks.
+ // Since we might scan a hardlink before its equivalent object is scanned
+ // we put them all in a list.
+ // After scanning is complete, we should have all the objects, so we run through this
+ // list and fix up all the chains.
+
+ switch(in->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+
+
+ if(in->variant.fileVariant.scannedFileSize < oh->fileSize)
+ {
+ in->variant.fileVariant.fileSize = oh->fileSize;
+ in->variant.fileVariant.scannedFileSize = in->variant.fileVariant.fileSize;
+ }
+
+
+
+ if(in->variant.fileVariant.shrinkSize > oh->fileSize)
+ {
+ in->variant.fileVariant.shrinkSize = oh->fileSize;
+ }
+
+
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
+ in->hardLinks.next = (struct list_head *)hardList;
+ hardList = in;
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing
+ in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
+ break;
+ }
+
+#if 0
+ if(parent == dev->deletedDir)
+ {
+ yaffs_DestroyObject(in);
+ bi->hasShrinkHeader = 1;
+ }
+#endif
+ //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
+ }
+ }
+ }
+
+ if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+ // If we got this far while scanning, then the block is fully allocated.
+ state = YAFFS_BLOCK_STATE_FULL;
+ }
+
+ bi->blockState = state;
+
+ // Now let's see if it was dirty
+ if( bi->pagesInUse == 0 &&
+ !bi->hasShrinkHeader &&
+ bi->blockState == YAFFS_BLOCK_STATE_FULL)
+ {
+ yaffs_BlockBecameDirty(dev,blk);
+ }
+
+ }
+
+ if(blockIndex)
+ {
+ YFREE(blockIndex);
+ }
+
+ // Ok, we've done all the scanning.
+
+ // Fix up the hard link chains.
+ // We should now have scanned all the objects, now it's time to add these
+ // hardlinks.
+ while(hardList)
+ {
+ hl = hardList;
+ hardList = (yaffs_Object *)(hardList->hardLinks.next);
+
+ in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);
+
+ if(in)
+ {
+ // Add the hardlink pointers
+ hl->variant.hardLinkVariant.equivalentObject=in;
+ list_add(&hl->hardLinks,&in->hardLinks);
+ }
+ else
+ {
+ //Todo Need to report/handle this better.
+ // Got a problem... hardlink to a non-existant object
+ hl->variant.hardLinkVariant.equivalentObject=NULL;
+ INIT_LIST_HEAD(&hl->hardLinks);
+
+ }
+
+ }
+
+ {
+ struct list_head *i;
+ struct list_head *n;
+
+ yaffs_Object *l;
+
+ // Soft delete all the unlinked files
+ list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
+ {
+ if(i)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ yaffs_DestroyObject(l);
+ }
+ }
+
+ // Soft delete all the deletedDir files
+ list_for_each_safe(i,n,&dev->deletedDir->variant.directoryVariant.children)
+ {
+ if(i)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ yaffs_DestroyObject(l);
+
+ }
+ }
+ }
+
+ yaffs_ReleaseTempBuffer(dev,chunkData,__LINE__);
+
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards ends" TENDSTR)));
+
+ return YAFFS_OK;
+}
+
+
+
+////////////////////////// Directory Functions /////////////////////////
+
+
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
+{
+
+ if(!directory)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: Trying to add an object to a null pointer directory" TENDSTR)));
+ YBUG();
+ }
+ if(directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: Trying to add an object to a non-directory" TENDSTR)));
+ YBUG();
+ }
+
+ if(obj->siblings.prev == NULL)
+ {
+ // Not initialised
+ INIT_LIST_HEAD(&obj->siblings);
+
}
else if(!list_empty(&obj->siblings))
{
yaffs_Object *l;
+
+ if(!directory)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: null pointer directory"TENDSTR)));
+ YBUG();
+ }
+ if(directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: non-directory"TENDSTR)));
+ YBUG();
+ }
+
sum = yaffs_CalcNameSum(name);
list_for_each(i,&directory->variant.directoryVariant.children)
struct list_head *i;
yaffs_Object *l;
+
+ if(!theDir)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: null pointer directory"TENDSTR)));
+ YBUG();
+ }
+ if(theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("tragedy: yaffs_FindObjectByName: non-directory"TENDSTR)));
+ YBUG();
+ }
list_for_each(i,&theDir->variant.directoryVariant.children)
{
case YAFFS_OBJECT_TYPE_SYMLINK: return DT_LNK; break;
case YAFFS_OBJECT_TYPE_HARDLINK: return DT_REG; break;
case YAFFS_OBJECT_TYPE_SPECIAL:
- if(S_ISFIFO(obj->st_mode)) return DT_FIFO;
- if(S_ISCHR(obj->st_mode)) return DT_CHR;
- if(S_ISBLK(obj->st_mode)) return DT_BLK;
- if(S_ISSOCK(obj->st_mode)) return DT_SOCK;
+ if(S_ISFIFO(obj->yst_mode)) return DT_FIFO;
+ if(S_ISCHR(obj->yst_mode)) return DT_CHR;
+ if(S_ISBLK(obj->yst_mode)) return DT_BLK;
+ if(S_ISSOCK(obj->yst_mode)) return DT_SOCK;
default: return DT_REG; break;
}
}
{
unsigned int valid = attr->ia_valid;
- if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode;
- if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;
- if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;
+ if(valid & ATTR_MODE) obj->yst_mode = attr->ia_mode;
+ if(valid & ATTR_UID) obj->yst_uid = attr->ia_uid;
+ if(valid & ATTR_GID) obj->yst_gid = attr->ia_gid;
- if(valid & ATTR_ATIME) obj->st_atime = Y_TIME_CONVERT(attr->ia_atime);
- if(valid & ATTR_CTIME) obj->st_ctime = Y_TIME_CONVERT(attr->ia_ctime);
- if(valid & ATTR_MTIME) obj->st_mtime = Y_TIME_CONVERT(attr->ia_mtime);
+ if(valid & ATTR_ATIME) obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime);
+ if(valid & ATTR_CTIME) obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime);
+ if(valid & ATTR_MTIME) obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime);
if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
- yaffs_UpdateObjectHeader(obj,NULL,1,0);
+ yaffs_UpdateObjectHeader(obj,NULL,1,0,0);
return YAFFS_OK;
{
unsigned int valid = 0;
- attr->ia_mode = obj->st_mode; valid |= ATTR_MODE;
- attr->ia_uid = obj->st_uid; valid |= ATTR_UID;
- attr->ia_gid = obj->st_gid; valid |= ATTR_GID;
+ attr->ia_mode = obj->yst_mode; valid |= ATTR_MODE;
+ attr->ia_uid = obj->yst_uid; valid |= ATTR_UID;
+ attr->ia_gid = obj->yst_gid; valid |= ATTR_GID;
- Y_TIME_CONVERT(attr->ia_atime)= obj->st_atime; valid |= ATTR_ATIME;
- Y_TIME_CONVERT(attr->ia_ctime) = obj->st_ctime; valid |= ATTR_CTIME;
- Y_TIME_CONVERT(attr->ia_mtime) = obj->st_mtime; valid |= ATTR_MTIME;
+ Y_TIME_CONVERT(attr->ia_atime)= obj->yst_atime; valid |= ATTR_ATIME;
+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; valid |= ATTR_CTIME;
+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; valid |= ATTR_MTIME;
attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
///////////////////////// Initialisation code ///////////////////////////
-int yaffs_CheckDevFunctions(const yaffs_Device *dev)
+static int yaffs_CheckDevFunctions(const yaffs_Device *dev)
{
// Common functions, gotta have
int extraBits;
int nBlocks;
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR)));
// Check stuff that must be set
if(!dev)
{
- T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Need a device\n" TENDSTR)));
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Need a device" TENDSTR)));
return YAFFS_FAIL;
}
+
+ dev->internalStartBlock = dev->startBlock;
+ dev->internalEndBlock = dev->endBlock;
+ dev->blockOffset = 0;
+ dev->chunkOffset = 0;
+ dev->nFreeChunks = 0;
+
+ if(dev->startBlock == 0)
+ {
+ dev->internalStartBlock = dev->startBlock + 1;
+ dev->internalEndBlock = dev->endBlock + 1;
+ dev->blockOffset = 1;
+ dev->chunkOffset = dev->nChunksPerBlock;
+ }
// Check geometry parameters.
(!dev->isYaffs2 && dev->nBytesPerChunk !=512) ||
dev->nChunksPerBlock < 2 ||
dev->nReservedBlocks < 2 ||
- dev->startBlock <= 0 ||
- dev->endBlock <= 0 ||
- dev->endBlock <= (dev->startBlock + dev->nReservedBlocks + 2) // otherwise it is too small
+ dev->internalStartBlock <= 0 ||
+ dev->internalEndBlock <= 0 ||
+ dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2) // otherwise it is too small
)
{
- T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: nand geometry problems\n" TENDSTR)));
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s " TENDSTR),
+ dev->nBytesPerChunk, dev->isYaffs2 ? "2" : ""));
return YAFFS_FAIL;
}
+ if(yaffs_InitialiseNAND(dev) != YAFFS_OK)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: InitialiseNAND failed" TENDSTR)));
+ return YAFFS_FAIL;
+ }
// Got the right mix of functions?
//
dev->isMounted = 1;
- nBlocks = dev->endBlock - dev->startBlock + 1;
+ nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
// OK now calculate a few things for the device
// Calculate chunkGroupBits.
- // We need to find the next power of 2 > than endBlock
+ // We need to find the next power of 2 > than internalEndBlock
- x = dev->nChunksPerBlock * (dev->endBlock+1);
+ x = dev->nChunksPerBlock * (dev->internalEndBlock+1);
for(bits = extraBits = 0; x > 1; bits++)
{
dev->tagsEccFixed=0;
dev->tagsEccUnfixed=0;
dev->nErasureFailures = 0;
+ dev->nErasedBlocks = 0;
+ dev->isDoingGC = 0;
//dev->localBuffer = YMALLOC(dev->nBytesPerChunk);
// Initialise temporary buffers
-
yaffs_InitialiseBlocks(dev,nBlocks);
yaffs_InitialiseTnodes(dev);
dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = dev->deletedDir = NULL;
dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR);
- dev->deletedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR);
+ dev->deletedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_DELETED, S_IFDIR);
dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);
dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR);
}
// Now scan the flash.
- yaffs_Scan(dev);
+
+ if(dev->isYaffs2)
+ yaffs_ScanBackwards(dev);
+ else
+ yaffs_Scan(dev);
// Zero out stats
dev->nPageReads = 0;
- dev->nPageWrites = 0;
+ dev->nPageWrites = 0;
dev->nBlockErasures = 0;
dev->nGCCopies = 0;
dev->nRetriedWrites = 0;
dev->nRetiredBlocks = 0;
-
+ yaffs_VerifyFreeChunks(dev);
+
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR)));
return YAFFS_OK;
}
#endif
-int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+static int yaffs_CountFreeChunks(yaffs_Device *dev)
{
int nFree;
- int pending;
int b;
- int nDirtyCacheChunks=0;
-
- yaffs_BlockInfo *blk;
-
- struct list_head *i;
- yaffs_Object *l;
+
+ yaffs_BlockInfo *blk;
+
- for(nFree = 0, b = dev->startBlock; b <= dev->endBlock; b++)
+ for(nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; b++)
{
blk = yaffs_GetBlockInfo(dev,b);
{
case YAFFS_BLOCK_STATE_EMPTY:
case YAFFS_BLOCK_STATE_ALLOCATING:
- case YAFFS_BLOCK_STATE_FULL: nFree += (dev->nChunksPerBlock - blk->pagesInUse); break;
+ case YAFFS_BLOCK_STATE_COLLECTING:
+ case YAFFS_BLOCK_STATE_FULL: nFree += (dev->nChunksPerBlock - blk->pagesInUse + blk->softDeletions); break;
default: break;
}
+
}
+ return nFree;
+}
- pending = 0;
-
- // To the free chunks add the chunks that are in the deleted unlinked files.
- list_for_each(i,&dev->deletedDir->variant.directoryVariant.children)
- {
- if(i)
- {
- 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;
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+ // This is what we report to the outside world
+
+ int nFree;
+ int nDirtyCacheChunks;
+
+#if 1
+ nFree = dev->nFreeChunks;
+#else
+ nFree = yaffs_CountFreeChunks(dev);
+#endif
// Now count the number of dirty chunks in the cache and subtract those
{
int i;
- for(i = 0; i < dev->nShortOpCaches; i++)
+ for( nDirtyCacheChunks = 0,i = 0; i < dev->nShortOpCaches; i++)
{
if(dev->srCache[i].dirty) nDirtyCacheChunks++;
}
}
+static int yaffs_freeVerificationFailures;
+static void yaffs_VerifyFreeChunks(yaffs_Device *dev)
+{
+ int counted = yaffs_CountFreeChunks(dev);
+
+ int difference = dev->nFreeChunks - counted;
+
+ if(difference)
+ {
+ T(YAFFS_TRACE_ALWAYS,(TSTR("Freechunks verification failure %d %d %d" TENDSTR),dev->nFreeChunks,counted,difference));
+ yaffs_freeVerificationFailures++;
+ }
+}
/////////////////// YAFFS test code //////////////////////////////////
#endif
-