*/\r
//yaffs_guts.c\r
\r
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.12 2002-11-08 07:19:41 charles Exp $";\r
+\r
#include "yportenv.h"\r
\r
#include "yaffsinterface.h"\r
#include "yaffs_guts.h"\r
\r
\r
+#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2\r
+\r
+\r
\r
// External functions for ECC on data\r
void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);\r
\r
\r
\r
-// Device info\r
-//static yaffs_Device *yaffs_device;\r
-//yaffs_Object *yaffs_rootDir;\r
-//yaffs_Object *yaffs_lostNFound;\r
-\r
-\r
-\r
// Local prototypes\r
static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);\r
static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);\r
-static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);\r
+static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);\r
static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan);\r
\r
static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);\r
static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);\r
-static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name);\r
+static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force);\r
static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);\r
static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);\r
static int yaffs_CheckStructures(void);\r
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit);\r
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in);\r
+\r
+static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo);\r
\r
// Robustification\r
static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND);\r
\r
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);\r
\r
+static int yaffs_UnlinkWorker(yaffs_Object *obj);\r
\r
\r
static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1);\r
#define yaffs_CheckFileSanity(in)\r
#endif\r
\r
-static int __inline__ yaffs_HashFunction(int n)\r
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);\r
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);\r
+\r
+\r
+static __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)\r
+{\r
+ if(blk < dev->startBlock || blk > dev->endBlock)\r
+ {\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: block %d is not valid" TENDSTR),blk));\r
+ YBUG();\r
+ }\r
+ return &dev->blockInfo[blk - dev->startBlock];\r
+}\r
+\r
+\r
+static __inline__ int yaffs_HashFunction(int n)\r
{\r
return (n % YAFFS_NOBJECT_BUCKETS);\r
}\r
\r
static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)\r
{\r
+ if(chunkInNAND < dev->startBlock * YAFFS_CHUNKS_PER_BLOCK)\r
+ {\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));\r
+ return YAFFS_FAIL;\r
+ }\r
+\r
dev->nPageWrites++;\r
return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);\r
}\r
\r
\r
-int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare,int doErrorCorrection)\r
+int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,\r
+ int chunkInNAND, \r
+ __u8 *data, \r
+ yaffs_Spare *spare, \r
+ int doErrorCorrection)\r
{\r
int retVal;\r
__u8 calcEcc[3];\r
\r
dev->nPageReads++;\r
\r
+ \r
+\r
+ \r
if(!spare && data)\r
{\r
// If we don't have a real spare, then we use a local one.\r
\r
if(eccResult1>0)\r
{\r
- T((TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));\r
+ T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));\r
+ dev->eccFixed++;\r
}\r
else if(eccResult1<0)\r
{\r
- T((TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));\r
+ dev->eccUnfixed++;\r
}\r
\r
if(eccResult2>0)\r
{\r
- T((TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));\r
+ dev->eccFixed++;\r
}\r
else if(eccResult2<0)\r
{\r
- T((TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));\r
+ dev->eccUnfixed++;\r
}\r
\r
if(eccResult1 || eccResult2)\r
yaffs_HandleReadDataError(dev,chunkInNAND);\r
}\r
}\r
+\r
return retVal;\r
}\r
\r
\r
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)\r
{\r
-#if 1\r
\r
static int init = 0;\r
static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];\r
if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return YAFFS_FAIL;\r
if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL;\r
\r
-#endif\r
\r
return YAFFS_OK;\r
\r
{\r
\r
// First check this chunk is erased...\r
+#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK\r
writeOk = yaffs_CheckChunkErased(dev,chunk);\r
- \r
- if(writeOk)\r
+#endif \r
+ if(!writeOk)\r
+ {\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));\r
+ }\r
+ else\r
{\r
writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare);\r
}\r
// NB We check a raw read without ECC correction applied\r
yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0);\r
\r
+#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY\r
if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare))\r
- {\r
+ {\r
// Didn't verify\r
- T((TSTR("**>> yaffs write failed on chunk %d" TENDSTR), chunk));\r
- // yaffs_DeleteChunk(dev,chunk);\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));\r
\r
writeOk = 0;\r
- } \r
+ } \r
+#endif \r
\r
}\r
if(writeOk)\r
\r
if(attempts > 1)\r
{\r
- T((TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));\r
dev->nRetriedWrites+= (attempts - 1); \r
}\r
\r
yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK, NULL , &spare);\r
yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK + 1, NULL , &spare);\r
\r
- dev->blockInfo[blockInNAND].blockState = YAFFS_BLOCK_STATE_DEAD;\r
+ yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;\r
dev->nRetiredBlocks++;\r
}\r
\r
// Set current write block to the new block\r
\r
dev->doingBufferedBlockRewrite = 0;\r
+ \r
+ return 1;\r
}\r
\r
\r
static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)\r
{\r
- \r
+ int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;\r
+\r
+ // Mark the block for retirement\r
+ yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;\r
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));\r
+\r
+\r
+ //TODO \r
// Just do a garbage collection on the affected block then retire the block\r
// NB recursion\r
}\r
\r
static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)\r
{\r
+ int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;\r
+\r
+ // Mark the block for retirement\r
+ yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;\r
+ // Delete the chunk\r
+ yaffs_DeleteChunk(dev,chunkInNAND);\r
}\r
\r
\r
static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1)\r
{\r
\r
+\r
if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 ||\r
s0->tagByte0 != s1->tagByte0 ||\r
s0->tagByte1 != s1->tagByte1 ||\r
__u16 i = 1;\r
\r
__u8 *bname = (__u8 *)name;\r
- \r
- while (*bname)\r
+ if(bname)\r
{\r
- sum += (*bname) * i;\r
- i++;\r
- bname++;\r
+ while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))\r
+ {\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ sum += toupper(*bname) * i;\r
+#else\r
+ sum += (*bname) * i;\r
+#endif\r
+ i++;\r
+ bname++;\r
+ }\r
}\r
return sum;\r
}\r
\r
+void yaffs_SetObjectName(yaffs_Object *obj, const char *name)\r
+{\r
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM\r
+ if(name && strlen(name) <= YAFFS_SHORT_NAME_LENGTH)\r
+ {\r
+ strcpy(obj->shortName,name);\r
+ }\r
+ else\r
+ {\r
+ obj->shortName[0]='\0';\r
+ }\r
+#endif\r
+ obj->sum = yaffs_CalcNameSum(name);\r
+}\r
\r
void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)\r
{\r
\r
for(i = 0; i < 8; i++)\r
{\r
- for(j = 1; j &0x7f; j<<=1)\r
+ for(j = 1; j &0xff; j<<=1)\r
{\r
bit++;\r
if(b[i] & j)\r
\r
}\r
\r
-void yaffs_CheckECCOnTags(yaffs_Tags *tags)\r
+int yaffs_CheckECCOnTags(yaffs_Tags *tags)\r
{\r
unsigned ecc = tags->ecc;\r
\r
\r
ecc ^= tags->ecc;\r
\r
- if(ecc)\r
+ if(ecc && ecc <= 64)\r
{\r
- // Needs fixing\r
+ // TODO: Handle the failure better. Retire?\r
unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;\r
\r
ecc--;\r
\r
// Now recvalc the ecc\r
yaffs_CalcTagsECC(tags);\r
+ \r
+ return 1; // recovered error\r
+ }\r
+ else if(ecc)\r
+ {\r
+ // Wierd ecc failure value\r
+ // TODO Need to do somethiong here\r
+ return -1; //unrecovered error\r
}\r
+ \r
+ return 0;\r
}\r
\r
\r
\r
if (!newTnodes)\r
{\r
- YALERT("Could not malloc tnodes");\r
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR)));\r
return YAFFS_FAIL;\r
}\r
\r
dev->nTnodesCreated += nTnodes;\r
\r
// Now add this bunch of tnodes to a list for freeing up.\r
-\r
+ // NB If we can't add this to the management list it isn't fatal\r
+ // but it just means we can't free this bunch of tnodes later.\r
tnl = YMALLOC(sizeof(yaffs_TnodeList));\r
if(!tnl)\r
{\r
- YALERT("Could not add tnodes to management list");\r
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR)));\r
+ \r
}\r
else\r
{\r
}\r
\r
\r
- YINFO("Tnodes created");\r
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR)));\r
\r
\r
return YAFFS_OK;\r
\r
}\r
\r
+#if 0\r
void yaffs_TnodeTest(yaffs_Device *dev)\r
{\r
\r
\r
}\r
}\r
+#endif\r
+\r
\r
////////////////// END OF TNODE MANIPULATION ///////////////////////////\r
\r
// Check sane level and chunk Id\r
if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)\r
{\r
- char str[50];\r
- sprintf(str,"Bad level %d",level);\r
- YALERT(str);\r
+// char str[50];\r
+// sprintf(str,"Bad level %d",level);\r
+// YALERT(str);\r
return NULL;\r
}\r
\r
if(chunkId > YAFFS_MAX_CHUNK_ID)\r
{\r
- char str[50];\r
- sprintf(str,"Bad chunkId %d",chunkId);\r
- YALERT(str);\r
+// char str[50];\r
+// sprintf(str,"Bad chunkId %d",chunkId);\r
+// YALERT(str);\r
return NULL;\r
}\r
\r
yaffs_Tnode *tn; \r
\r
int requiredTallness;\r
+ int i;\r
+ int l;\r
\r
- __u32 i;\r
- __u32 l;\r
- \r
+ __u32 x;\r
+ \r
\r
//T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId));\r
\r
// Check sane level and page Id\r
if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)\r
{\r
- char str[50];\r
- sprintf(str,"Bad level %d",fStruct->topLevel);\r
- YALERT(str);\r
+// char str[50];\r
+// sprintf(str,"Bad level %d",fStruct->topLevel);\r
+// YALERT(str);\r
return NULL;\r
}\r
\r
if(chunkId > YAFFS_MAX_CHUNK_ID)\r
{\r
- char str[50];\r
- sprintf(str,"Bad chunkId %d",chunkId);\r
- YALERT(str);\r
+// char str[50];\r
+// sprintf(str,"Bad chunkId %d",chunkId);\r
+// YALERT(str);\r
return NULL;\r
}\r
\r
// First check we're tall enough (ie enough topLevel)\r
\r
- i = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);\r
+ x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);\r
requiredTallness = 0;\r
- while(i)\r
+ while(x)\r
{\r
- i >>= YAFFS_TNODES_INTERNAL_BITS;\r
+ x >>= YAFFS_TNODES_INTERNAL_BITS;\r
requiredTallness++;\r
}\r
\r
}\r
else\r
{\r
- YALERT("No more tnodes");\r
+ T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR)));\r
}\r
}\r
\r
tn = fStruct->top;\r
while (l > 0 && tn)\r
{\r
- i = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & \r
+ x = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & \r
YAFFS_TNODES_INTERNAL_MASK;\r
\r
//T((TSTR(" [%d:%d]"),l,i));\r
\r
- if(!tn->internal[i])\r
+ if(!tn->internal[x])\r
{\r
//T((TSTR(" added")));\r
\r
- tn->internal[i] = yaffs_GetTnode(dev);\r
+ tn->internal[x] = yaffs_GetTnode(dev);\r
}\r
\r
- tn = tn->internal[i];\r
+ tn = tn->internal[x];\r
l--;\r
\r
}\r
return tn; \r
}\r
\r
-// DeleteWorker scans backwards through the tnode tree and delets all the\r
+// DeleteWorker scans backwards through the tnode tree and deletes all the\r
// chunks and tnodes in the file\r
+// Returns 1 if the tree was deleted. Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.\r
\r
-static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)\r
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit)\r
{\r
int i;\r
int chunkInInode;\r
yaffs_Tags tags;\r
int found;\r
int chunkDeleted;\r
+ int allDone = 1;\r
\r
\r
if(tn)\r
if(level > 0)\r
{\r
\r
- for(i = YAFFS_NTNODES_INTERNAL -1; i >= 0; i--)\r
+ for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)\r
{\r
if(tn->internal[i])\r
{\r
- yaffs_DeleteWorker(in,tn->internal[i],level - 1,\r
- (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i );\r
- yaffs_FreeTnode(in->myDev,tn->internal[i]);\r
- tn->internal[i] = NULL;\r
+ if(limit && (*limit) < 0)\r
+ {\r
+ allDone = 0;\r
+ }\r
+ else\r
+ {\r
+ allDone = yaffs_DeleteWorker(in,tn->internal[i],level - 1,\r
+ (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit);\r
+ }\r
+ if(allDone)\r
+ {\r
+ yaffs_FreeTnode(in->myDev,tn->internal[i]);\r
+ tn->internal[i] = NULL;\r
+ }\r
}\r
\r
}\r
+ return (allDone) ? 1 : 0;\r
}\r
else if(level == 0)\r
{\r
- for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--)\r
+ for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--) //NB Don't apply the limit here, always delete a whole level0\r
{\r
if(tn->level0[i])\r
{\r
if(found)\r
{\r
yaffs_DeleteChunk(in->myDev,theChunk);\r
+ in->nDataChunks--;\r
+ if(limit)\r
+ { \r
+ *limit = *limit-1;\r
+ }\r
\r
}\r
\r
}\r
\r
}\r
+ return 1;\r
+\r
\r
}\r
\r
}\r
\r
+ return 1;\r
+ \r
}\r
\r
\r
\r
\r
\r
+\r
/////////////////////// End of File Structure functions. /////////////////\r
\r
// yaffs_CreateFreeObjects creates a bunch more objects and\r
\r
if (!newObjects)\r
{\r
- YALERT("Could not allocate more objects");\r
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR)));\r
return YAFFS_FAIL;\r
}\r
\r
list = YMALLOC(sizeof(yaffs_ObjectList));\r
if(!list)\r
{\r
- YALERT("Could not add Objects to management list");\r
+ T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR)));\r
}\r
else\r
{\r
}\r
\r
\r
- YINFO("Objects created");\r
- \r
\r
return YAFFS_OK;\r
}\r
// Now sweeten it up...\r
\r
memset(tn,0,sizeof(yaffs_Object));\r
+ tn->myDev = dev;\r
tn->chunkId = -1;\r
tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;\r
INIT_LIST_HEAD(&(tn->hardLinks));\r
obj->fake = 1; // it is fake so it has no NAND presence...\r
obj->renameAllowed= 0; // ... and we're not allowed to rename it...\r
obj->unlinkAllowed= 0; // ... or unlink it\r
+ obj->deleted = 0;\r
+ obj->unlinked = 0;\r
obj->st_mode = mode;\r
obj->myDev = dev;\r
obj->chunkId = 0; // Not a valid chunk.\r
int found = 0;\r
struct list_head *i;\r
\r
- int n = bucket;\r
+ __u32 n = (__u32)bucket;\r
\r
//yaffs_CheckObjectHashSanity(); \r
\r
\r
if(!list_empty(&in->hashLink))\r
{\r
- YINFO("!!!");\r
+ //YINFO("!!!");\r
}\r
- \r
+\r
\r
list_add(&in->hashLink,&dev->objectBucket[bucket].list);\r
dev->objectBucket[bucket].count++;\r
\r
}\r
\r
-yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number)\r
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number)\r
{\r
int bucket = yaffs_HashFunction(number);\r
struct list_head *i;\r
theObject->renameAllowed = 1;\r
theObject->unlinkAllowed = 1;\r
theObject->objectId = number;\r
- theObject->myDev = dev;\r
yaffs_HashObject(theObject);\r
theObject->variantType = type;\r
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ yfsd_WinFileTimeNow(theObject->win_atime);\r
+ theObject->win_ctime[0] = theObject->win_mtime[0] = theObject->win_atime[0];\r
+ theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];\r
\r
+#else\r
+ theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;\r
+#endif\r
switch(type)\r
{\r
case YAFFS_OBJECT_TYPE_FILE: \r
in->variantType = type;\r
\r
in->st_mode = mode;\r
+ \r
+#ifdef CONFIG_YAFFS_WINCE\r
+ yfsd_WinFileTimeNow(in->win_atime);\r
+ in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];\r
+ in->win_ctime[1] = in->win_mtime[1] = in->win_atime[0];\r
+ \r
+#else\r
+ in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;\r
in->st_rdev = rdev;\r
in->st_uid = uid;\r
in->st_gid = gid;\r
- in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;\r
- \r
+#endif \r
in->nDataChunks = 0;\r
\r
- in->sum = yaffs_CalcNameSum(name);\r
+ yaffs_SetObjectName(in,name);\r
in->dirty = 1;\r
\r
yaffs_AddObjectToDirectory(parent,in);\r
break;\r
}\r
\r
- if(yaffs_UpdateObjectHeader(in,name) < 0)\r
+ if(yaffs_GetNumberOfFreeChunks(dev) <= 0 ||\r
+ yaffs_UpdateObjectHeader(in,name,0) < 0)\r
{\r
// Could not create the object header, fail the creation\r
yaffs_UnlinkWorker(in);\r
}\r
\r
\r
-static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName)\r
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName,int force)\r
{\r
- //yaffs_Device *dev = obj->myDev;\r
+ int unlinkOp;\r
\r
if(newDir == NULL)\r
{\r
newDir = obj->parent; // use the old directory\r
}\r
+\r
+ unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE);\r
\r
- // Only proceed if the new name does not exist and\r
- // if we're putting it into a directory.\r
- if(!yaffs_FindObjectByName(newDir,newName) &&\r
- newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)\r
+ // If the object is a file going into the unlinked directory, then it is OK to just stuff it in since\r
+ // duplicate names are allowed.\r
+ // Otherwise only proceed if the new name does not exist and if we're putting it into a directory.\r
+ if( (unlinkOp|| \r
+ force || \r
+ !yaffs_FindObjectByName(newDir,newName)) &&\r
+ newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)\r
{\r
- obj->sum = yaffs_CalcNameSum(newName);\r
+ yaffs_SetObjectName(obj,newName);\r
obj->dirty = 1;\r
+ \r
yaffs_AddObjectToDirectory(newDir,obj);\r
\r
- if(yaffs_UpdateObjectHeader(obj,newName) >= 0)\r
+ if(unlinkOp) obj->unlinked = 1;\r
+ \r
+ \r
+ if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0)\r
{\r
return YAFFS_OK;\r
}\r
return YAFFS_FAIL;\r
}\r
\r
+\r
+\r
int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)\r
{\r
yaffs_Object *obj;\r
+ int force = 0;\r
+ \r
+#ifdef CONFIG_YAFFS_WINCE\r
+ // Special case for WinCE.\r
+ // While look-up is case insensitive, the name isn't.\r
+ // THerefore we might want to change x.txt to X.txt\r
+ if(oldDir == newDir && _stricmp(oldName,newName) == 0)\r
+ {\r
+ force = 1;\r
+ } \r
+#endif\r
\r
obj = yaffs_FindObjectByName(oldDir,oldName);\r
if(obj && obj->renameAllowed)\r
{\r
- return yaffs_ChangeObjectName(obj,newDir,newName);\r
+ return yaffs_ChangeObjectName(obj,newDir,newName,force);\r
}\r
return YAFFS_FAIL;\r
}\r
\r
if(countEm != dev->objectBucket[bucket].count)\r
{\r
- YALERT("Inode hash inconsistency");\r
+ T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR)));\r
ok = YAFFS_FAIL;\r
}\r
}\r
return ok;\r
}\r
\r
+#if 0\r
void yaffs_ObjectTest(yaffs_Device *dev)\r
{\r
yaffs_Object *in[1000];\r
\r
}\r
\r
-\r
+#endif\r
\r
/////////////////////////// Block Management and Page Allocation ///////////////////\r
\r
\r
-static void yaffs_InitialiseBlocks(yaffs_Device *dev)\r
+static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)\r
{\r
- //Todo we're assuming the malloc will pass.\r
- dev->blockInfo = YMALLOC(dev->nBlocks * sizeof(yaffs_BlockInfo));\r
- memset(dev->blockInfo,0,dev->nBlocks * sizeof(yaffs_BlockInfo));\r
dev->allocationBlock = -1; // force it to get a new one\r
+ //Todo we're assuming the malloc will pass.\r
+ dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));\r
+ if(dev->blockInfo)\r
+ {\r
+ memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));\r
+ return YAFFS_OK;\r
+ }\r
+ return YAFFS_FAIL;\r
+ \r
}\r
\r
static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)\r
int i;\r
int dirtiest = -1;\r
int pagesInUse = 100; // silly big number\r
+ yaffs_BlockInfo *bi;\r
\r
for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)\r
{\r
b++;\r
- if (b > dev->endBlock)\r
+ if ( b < dev->startBlock || b > dev->endBlock)\r
{\r
b = dev->startBlock;\r
}\r
+\r
+ if(b < dev->startBlock || b > dev->endBlock)\r
+ {\r
+ T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));\r
+ YBUG();\r
+ }\r
+ \r
+ bi = yaffs_GetBlockInfo(dev,b);\r
\r
- if(dev->blockInfo[b].blockState == YAFFS_BLOCK_STATE_FULL &&\r
- (dev->blockInfo)[b].pagesInUse < pagesInUse)\r
+ if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&\r
+ bi->pagesInUse < pagesInUse)\r
{\r
dirtiest = b;\r
- pagesInUse = (dev->blockInfo)[b].pagesInUse;\r
+ pagesInUse = bi->pagesInUse;\r
}\r
}\r
\r
}\r
\r
\r
-static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve)\r
+static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)\r
+{\r
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,blockNo);\r
+ \r
+ int erasedOk = 0;\r
+ \r
+ // If the block is still healthy erase it and mark as clean.\r
+ // If the block has had a data failure, then retire it.\r
+ bi->blockState = YAFFS_BLOCK_STATE_DIRTY;\r
+\r
+ if(!bi->needsRetiring)\r
+ {\r
+ erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);\r
+ if(!erasedOk)\r
+ {\r
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo));\r
+ }\r
+ }\r
+ \r
+ if( erasedOk )\r
+ {\r
+ bi->blockState = YAFFS_BLOCK_STATE_EMPTY;\r
+ dev->nErasedBlocks++;\r
+ bi->pagesInUse = 0;\r
+ bi->pageBits = 0;\r
+ \r
+ T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));\r
+ }\r
+ else\r
+ {\r
+ yaffs_RetireBlock(dev,blockNo);\r
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo));\r
+ }\r
+}\r
+\r
+\r
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev)\r
{\r
int i;\r
+ yaffs_BlockInfo *bi;\r
\r
- if(useReserve && dev->nErasedBlocks < 1)\r
+ if(dev->nErasedBlocks < 1)\r
{\r
// Hoosterman we've got a problem.\r
// Can't get space to gc\r
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during gc" TENDSTR)));\r
+\r
return -1;\r
}\r
- else if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)\r
- {\r
- // We are not in GC, so we hold some in reserve so we can get\r
- // a gc done.\r
- }\r
\r
// Find an empty block.\r
\r
for(i = dev->startBlock; i <= dev->endBlock; i++)\r
{\r
- \r
- if(dev->blockInfo[i].blockState == YAFFS_BLOCK_STATE_EMPTY)\r
+ bi = yaffs_GetBlockInfo(dev,i);\r
+ if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)\r
{\r
- dev->blockInfo[i].blockState = YAFFS_BLOCK_STATE_ALLOCATING;\r
- dev->nErasedBlocks--;\r
- if(dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)\r
- {\r
- dev->garbageCollectionRequired = 1;\r
- }\r
- \r
+ bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;\r
+ dev->nErasedBlocks--; \r
return i;\r
}\r
}\r
}\r
\r
\r
-static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)\r
-{\r
- yaffs_BlockInfo *bi = &dev->blockInfo[blockNo];\r
- \r
- // Mark as dirty.\r
- // If the block is still healthy erase it and mark as clean.\r
- // If the block has had a data failure, then retire it.\r
- bi->blockState = YAFFS_BLOCK_STATE_DIRTY;\r
- \r
- if(!bi->needsRetiring && yaffs_EraseBlockInNAND(dev,blockNo))\r
- {\r
- bi->blockState = YAFFS_BLOCK_STATE_EMPTY;\r
- dev->nErasedBlocks++;\r
- bi->pagesInUse = 0;\r
- bi->pageBits = 0;\r
- \r
- T((TSTR("Erased block %d" TENDSTR),blockNo));\r
- }\r
- else\r
- {\r
- yaffs_RetireBlock(dev,blockNo);\r
- T((TSTR("**>> Block %d retired" TENDSTR),blockNo));\r
- }\r
-}\r
-\r
\r
static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)\r
{\r
int retVal;\r
+ yaffs_BlockInfo *bi;\r
\r
if(dev->allocationBlock < 0)\r
{\r
// Get next block to allocate off\r
- dev->allocationBlock = yaffs_FindBlockForAllocation(dev,useReserve);\r
+ dev->allocationBlock = yaffs_FindBlockForAllocation(dev);\r
dev->allocationPage = 0;\r
}\r
\r
+ if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)\r
+ {\r
+ // Not enough space to allocate unless we're allowed to use the reserve.\r
+ return -1;\r
+ }\r
+ \r
// Next page please....\r
if(dev->allocationBlock >= 0)\r
{\r
+ bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);\r
+ \r
retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) + \r
dev->allocationPage;\r
- dev->blockInfo[dev->allocationBlock].pagesInUse++;\r
- dev->blockInfo[dev->allocationBlock].pageBits |= \r
- (1 << (dev->allocationPage));\r
+ bi->pagesInUse++;\r
+ bi->pageBits |= (1 << (dev->allocationPage));\r
\r
dev->allocationPage++;\r
\r
// If the block is full set the state to full\r
if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK)\r
{\r
- dev->blockInfo[dev->allocationBlock].blockState = YAFFS_BLOCK_STATE_FULL;\r
+ bi->blockState = YAFFS_BLOCK_STATE_FULL;\r
dev->allocationBlock = -1;\r
}\r
\r
-#ifdef YAFFS_PARANOID\r
- if(yaffs_CheckChunkErased(dev,retVal) == YAFFS_FAIL)\r
- {\r
- T((TSTR("..................Trying to allocate non-erased page %d" TENDSTR),retVal));\r
- }\r
-#endif \r
+\r
return retVal;\r
\r
}\r
- T((TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));\r
+ T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));\r
\r
return -1; \r
}\r
\r
+// To determine if we have enough space we just look at the \r
+// number of erased blocks.\r
+// The cache is allowed to use reserved blocks.\r
+\r
+int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)\r
+{\r
+ return (dev->nErasedBlocks >= YAFFS_RESERVED_BLOCKS);\r
+}\r
+\r
\r
int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)\r
{\r
yaffs_Tags tags;\r
__u8 buffer[YAFFS_BYTES_PER_CHUNK];\r
\r
- yaffs_BlockInfo *bi = &dev->blockInfo[block];\r
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);\r
\r
yaffs_Object *object;\r
\r
\r
yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1);\r
\r
- yaffs_GetTagsFromSpare(&spare,&tags);\r
- tags.serialNumber++;\r
- yaffs_LoadTagsIntoSpare(&spare,&tags);\r
+ yaffs_GetTagsFromSpare(dev,&spare,&tags);\r
\r
-#if 0\r
- newChunk = yaffs_AllocatePage(dev,1);\r
- if(newChunk < 0)\r
+ object = yaffs_FindObjectByNumber(dev,tags.objectId);\r
+ \r
+ if(object && object->deleted && tags.chunkId != 0)\r
{\r
- return YAFFS_FAIL;\r
+ // Data chunk in a deleted file, throw it away\r
+ // It's a deleted data chunk,\r
+ // No need to copy this, just forget about it and fix up the\r
+ // object.\r
+ \r
+ yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); \r
+ object->nDataChunks--;\r
}\r
-\r
- yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare);\r
-\r
-#else\r
- newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);\r
-#endif\r
- if(newChunk < 0)\r
+ else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)\r
{\r
- return YAFFS_FAIL;\r
+ // Deleted object header with no data chunks.\r
+ // Can be discarded\r
+ object->chunkId = 0;\r
+ //Todo some clean up\r
+ \r
}\r
+ else if(object)\r
+ {\r
+ // It's either a data chunk in a live file or\r
+ // an ObjectHeader, so we're interested in it.\r
+ // NB Need to keep the ObjectHeaders of deleted files\r
+ // until the whole file has been deleted off\r
+ tags.serialNumber++;\r
+ yaffs_LoadTagsIntoSpare(&spare,&tags);\r
\r
- object = yaffs_FindObjectByNumber(dev,tags.objectId);\r
+\r
+ newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);\r
+ \r
+ if(newChunk < 0)\r
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
\r
- // Ok, now fix up the Tnodes etc.\r
+ // Ok, now fix up the Tnodes etc.\r
\r
- if(tags.chunkId == 0)\r
- {\r
- // It's a header\r
- object->chunkId = newChunk;\r
- }\r
- else\r
- {\r
- // It's a data chunk\r
- yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);\r
-\r
+ if(tags.chunkId == 0)\r
+ {\r
+ // It's a header\r
+ object->chunkId = newChunk;\r
+ }\r
+ else\r
+ {\r
+ // It's a data chunk\r
+ yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);\r
+ }\r
}\r
\r
yaffs_DeleteChunk(dev,oldChunk); \r
return YAFFS_OK;\r
}\r
\r
-int yaffs_CheckGarbageCollection(yaffs_Device *dev)\r
+\r
+static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)\r
+{\r
+ // todo find a file to delete\r
+ struct list_head *i; \r
+ yaffs_Object *l;\r
+\r
+\r
+ // To the free chunks add the chunks that are in the deleted unlinked files.\r
+ list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)\r
+ {\r
+ l = list_entry(i, yaffs_Object,siblings);\r
+ if(l->deleted)\r
+ {\r
+ return l; \r
+ }\r
+ } \r
+ return NULL;\r
+}\r
+\r
+static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)\r
+{\r
+ // This does background deletion on unlinked files.. only deleted ones.\r
+ // If we don't have a file we're working on then find one\r
+ if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0)\r
+ {\r
+ dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev);\r
+ }\r
+ \r
+ // OK, we're working on a file...\r
+ if(dev->unlinkedDeletion)\r
+ {\r
+ yaffs_Object *obj = dev->unlinkedDeletion;\r
+ int delresult;\r
+ int limit; // Number of chunks to delete in a file.\r
+ // NB this can be exceeded, but not by much.\r
+ \r
+ limit = 5;\r
+#if 0 \r
+ if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))\r
+ {\r
+ limit = 50; // Doing GC soon, so dig deeper \r
+ }\r
+ else\r
+ {\r
+ limit = 5;\r
+ }\r
+#endif\r
+ \r
+ delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);\r
+ \r
+ if(obj->nDataChunks == 0)\r
+ {\r
+ // Done all the deleting of data chunks.\r
+ // Now dump the header and clean up\r
+ yaffs_FreeTnode(dev,obj->variant.fileVariant.top);\r
+ yaffs_DoGenericObjectDeletion(obj);\r
+ dev->nDeletedFiles--;\r
+ dev->nUnlinkedFiles--;\r
+ dev->nBackgroundDeletions++;\r
+ dev->unlinkedDeletion = NULL; \r
+ }\r
+ }\r
+}\r
+\r
+\r
+\r
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)\r
{\r
int block;\r
\r
+ yaffs_DoUnlinkedFileDeletion(dev);\r
+ \r
+ if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))\r
+ {\r
+ dev->garbageCollectionRequired = 1;\r
+ } \r
+ \r
if(dev->garbageCollectionRequired)\r
{\r
+ dev->garbageCollections++;\r
dev->garbageCollectionRequired = 0;\r
if(dev->blockSelectedForGC >= 0)\r
{\r
sparePtr->tagByte7 = tu->asBytes[7];\r
}\r
\r
-static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)\r
+static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)\r
{\r
yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;\r
+ int result;\r
\r
tu->asBytes[0]= sparePtr->tagByte0;\r
tu->asBytes[1]= sparePtr->tagByte1;\r
tu->asBytes[6]= sparePtr->tagByte6;\r
tu->asBytes[7]= sparePtr->tagByte7;\r
\r
- yaffs_CheckECCOnTags(tagsPtr);\r
+ result = yaffs_CheckECCOnTags(tagsPtr);\r
+ if(result> 0)\r
+ {\r
+ dev->tagsEccFixed++;\r
+ }\r
+ else if(result <0)\r
+ {\r
+ dev->tagsEccUnfixed++;\r
+ }\r
}\r
\r
static void yaffs_SpareInitialise(yaffs_Spare *spare)\r
if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare,1) == YAFFS_OK)\r
{\r
*chunkDeleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;\r
- yaffs_GetTagsFromSpare(&spare,tags);\r
+ yaffs_GetTagsFromSpare(dev,&spare,tags);\r
return YAFFS_OK;\r
}\r
else\r
}\r
\r
\r
-#if YAFFS_PARANOID\r
+#ifdef YAFFS_PARANOID\r
\r
static int yaffs_CheckFileSanity(yaffs_Object *in)\r
{\r
if(existingChunk <=0)\r
{\r
//Hoosterman - how did this happen?\r
- // todo\r
+ \r
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: existing chunk < 0 in scan" TENDSTR)));\r
+\r
}\r
\r
\r
int block;\r
int page;\r
yaffs_Spare spare;\r
+ yaffs_BlockInfo *bi;\r
\r
if(chunkId <= 0) return; \r
\r
\r
yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);\r
yaffs_HandleUpdateChunk(dev,chunkId,&spare);\r
+ bi = yaffs_GetBlockInfo(dev,block);\r
\r
\r
// Pull out of the management area.\r
// If the whole block became dirty, this will kick off an erasure.\r
- if( dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_ALLOCATING ||\r
- dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)\r
+ if( bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||\r
+ bi->blockState == YAFFS_BLOCK_STATE_FULL)\r
{\r
dev->nFreeChunks++;\r
\r
- dev->blockInfo[block].pageBits &= ~(1 << page);\r
- dev->blockInfo[block].pagesInUse--;\r
+ bi->pageBits &= ~(1 << page);\r
+ bi->pagesInUse--;\r
\r
- if( dev->blockInfo[block].pagesInUse == 0 &&\r
- dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)\r
+ if(bi->pagesInUse == 0 &&\r
+ bi->blockState == YAFFS_BLOCK_STATE_FULL)\r
{\r
yaffs_BlockBecameDirty(dev,block);\r
}\r
\r
yaffs_CalcTagsECC(&newTags);\r
\r
- \r
- #if 0\r
- // Create new chunk in NAND\r
- newChunkId = yaffs_AllocatePage(dev,useReserve);\r
- \r
- \r
- if(newChunkId >= 0)\r
- {\r
- \r
-\r
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags);\r
- \r
- yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId);\r
- \r
- \r
- if(prevChunkId >= 0)\r
- {\r
- yaffs_DeleteChunk(dev,prevChunkId);\r
- \r
- }\r
- \r
- yaffs_CheckFileSanity(in);\r
- \r
- return newChunkId;\r
- }\r
-\r
- \r
- return -1;\r
-#else\r
-\r
newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);\r
if(newChunkId >= 0)\r
{\r
}\r
return newChunkId;\r
\r
-#endif\r
+\r
\r
\r
\r
// UpdateObjectHeader updates the header on NAND for an object.\r
// If name is not NULL, then that new name is used.\r
//\r
-int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name)\r
+int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)\r
{\r
\r
yaffs_Device *dev = in->myDev;\r
\r
yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;\r
yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;\r
+\r
\r
- if(!in->fake)\r
+ if(!in->fake || force)\r
{\r
\r
yaffs_CheckGarbageCollection(dev); \r
\r
// Header data\r
oh->type = in->variantType;\r
- \r
+ \r
oh->st_mode = in->st_mode;\r
+\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ oh->win_atime[0] = in->win_atime[0];\r
+ oh->win_ctime[0] = in->win_ctime[0];\r
+ oh->win_mtime[0] = in->win_mtime[0];\r
+ oh->win_atime[1] = in->win_atime[1];\r
+ oh->win_ctime[1] = in->win_ctime[1];\r
+ oh->win_mtime[1] = in->win_mtime[1];\r
+#else\r
oh->st_uid = in->st_uid;\r
oh->st_gid = in->st_gid;\r
oh->st_atime = in->st_atime;\r
oh->st_mtime = in->st_mtime;\r
oh->st_ctime = in->st_ctime;\r
oh->st_rdev = in->st_rdev;\r
- \r
- oh->parentObjectId = in->parent->objectId;\r
- oh->sum = in->sum;\r
+#endif \r
+ if(in->parent)\r
+ {\r
+ oh->parentObjectId = in->parent->objectId;\r
+ }\r
+ else\r
+ {\r
+ oh->parentObjectId = 0;\r
+ }\r
+ \r
+ //oh->sum = in->sum;\r
if(name && *name)\r
{\r
memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);\r
strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);\r
}\r
- else\r
+ else if(prevChunkId)\r
{ \r
memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);\r
}\r
+ else\r
+ {\r
+ memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); \r
+ }\r
\r
switch(in->variantType)\r
{\r
\r
yaffs_CalcTagsECC(&newTags);\r
\r
- \r
- \r
-#if 0\r
+\r
// Create new chunk in NAND\r
- newChunkId = yaffs_AllocatePage(dev,1);\r
+ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);\r
\r
if(newChunkId >= 0)\r
{\r
-\r
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags);\r
\r
in->chunkId = newChunkId; \r
\r
}\r
\r
in->dirty = 0;\r
- return newChunkId;\r
}\r
- \r
- return -1;\r
-#else\r
- // Create new chunk in NAND\r
- newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);\r
- \r
- if(newChunkId >= 0)\r
- {\r
\r
- in->chunkId = newChunkId; \r
+ return newChunkId;\r
+\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+/////////////////////// Short Operations Cache ////////////////////////////////\r
+// In many siturations where there is no high level buffering (eg WinCE) a lot of\r
+// reads might be short sequential reads, and a lot of writes may be short \r
+// sequential writes. eg. scanning/writing a jpeg file.\r
+// In these cases, a short read/write cache can provide a huge perfomance benefit \r
+// with dumb-as-a-rock code.\r
+// There are a limited number (~10) of cache chunks per device so that we don't\r
+// need a very intelligent search.\r
+\r
+\r
+#ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
+\r
+\r
+static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)\r
+{\r
+ yaffs_Device *dev = obj->myDev;\r
+ int lowest;\r
+ int i;\r
+ yaffs_ChunkCache *cache;\r
+ int chunkWritten;\r
+ int nBytes;\r
+ \r
+ do{\r
+ cache = NULL;\r
\r
- if(prevChunkId >= 0)\r
+ // Find the dirty cache for this object with the lowest chunk id.\r
+ for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ if(dev->srCache[i].object == obj &&\r
+ dev->srCache[i].dirty)\r
{\r
- yaffs_DeleteChunk(dev,prevChunkId);\r
+ if(!cache || dev->srCache[i].chunkId < lowest)\r
+ {\r
+ cache = &dev->srCache[i];\r
+ lowest = cache->chunkId;\r
+ }\r
}\r
+ }\r
\r
- in->dirty = 0;\r
+ if(cache)\r
+ {\r
+ //Write it out\r
+\r
+ nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);\r
+ \r
+ if(nBytes > YAFFS_BYTES_PER_CHUNK)\r
+ {\r
+ nBytes= YAFFS_BYTES_PER_CHUNK;\r
+ }\r
+ \r
+ chunkWritten = yaffs_WriteChunkDataToObject(cache->object,\r
+ cache->chunkId,\r
+ cache->data,\r
+ nBytes,1);\r
+\r
+ cache->dirty = 0;\r
}\r
\r
- return newChunkId;\r
+ } while(cache && chunkWritten > 0);\r
+ \r
+ if(cache)\r
+ {\r
+ //Hoosterman, disk full while writing cache out.\r
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: no space during caceh write" TENDSTR)));\r
\r
-#endif\r
- }\r
- return 0;\r
+ } \r
+ \r
+}\r
+\r
+\r
+// Grab us a chunk for use.\r
+// First look for an empty one. \r
+// Then look for the least recently used non-dirty one.\r
+// Then look for the least recently used dirty one...., flush and look again.\r
+static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)\r
+{\r
+ int i;\r
+ int usage;\r
+ int theOne;\r
+ \r
+ for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ if(!dev->srCache[i].object)\r
+ {\r
+ //T(("Grabbing empty %d\n",i));\r
+ \r
+ return &dev->srCache[i];\r
+ }\r
+ }\r
+ \r
+ theOne = -1; \r
+ usage = 0; // just to stop the compiler grizzling\r
+ \r
+ for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ if(!dev->srCache[i].dirty &&\r
+ ((dev->srCache[i].lastUse < usage && theOne >= 0)|| \r
+ theOne < 0))\r
+ {\r
+ usage = dev->srCache[i].lastUse;\r
+ theOne = i;\r
+ }\r
+ }\r
+ \r
+ //T(("Grabbing non-empty %d\n",theOne));\r
+ return theOne >= 0 ? &dev->srCache[theOne] : NULL;\r
+ \r
+}\r
+\r
+\r
+static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)\r
+{\r
+ yaffs_ChunkCache *cache;\r
+ yaffs_Object *theObj;\r
+ int usage;\r
+ int i;\r
+ \r
+ // Try find a non-dirty one...\r
+ \r
+ cache = yaffs_GrabChunkCacheWorker(dev);\r
+ \r
+ if(!cache)\r
+ {\r
+ // They were all dirty, find the last recently used object and flush\r
+ // its cache, then find again.\r
+ // NB what's here is not very accurate, we actually flush the object\r
+ // the last recently used page.\r
+ \r
+ theObj = dev->srCache[0].object;\r
+ usage = dev->srCache[0].lastUse;\r
+ \r
+ for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ if( dev->srCache[i].object && \r
+ dev->srCache[i].lastUse < usage)\r
+ {\r
+ usage = dev->srCache[i].lastUse;\r
+ theObj = dev->srCache[i].object;\r
+ }\r
+ }\r
+ \r
+ yaffs_FlushFilesChunkCache(theObj);\r
+ \r
+ // Try again\r
+ cache = yaffs_GrabChunkCacheWorker(dev);\r
+ }\r
+ \r
+ return cache;\r
+\r
+}\r
+\r
+\r
+// Find a cached chunk\r
+static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId)\r
+{\r
+ yaffs_Device *dev = obj->myDev;\r
+ int i;\r
+ \r
+ for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ if(dev->srCache[i].object == obj && \r
+ dev->srCache[i].chunkId == chunkId)\r
+ {\r
+ dev->cacheHits++;\r
+ \r
+ return &dev->srCache[i];\r
+ }\r
+ }\r
+ \r
+ return NULL;\r
+}\r
+\r
+// Mark the chunk for the least recently used algorithym\r
+static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)\r
+{\r
+ if( dev->srLastUse < 0 || \r
+ dev->srLastUse > 100000000)\r
+ {\r
+ // Reset the cache usages\r
+ int i;\r
+ for(i = 1; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ dev->srCache[i].lastUse = 0;\r
+ }\r
+ dev->srLastUse = 0;\r
+ }\r
+\r
+ dev->srLastUse++;\r
+ \r
+ cache->lastUse = dev->srLastUse;\r
+\r
+ if(isAWrite)\r
+ {\r
+ cache->dirty = 1;\r
+ }\r
+}\r
+\r
+// Invalidate a single cache page.\r
+// Do this when a whole page gets written,\r
+// ie the short cache for this page is no longer valid.\r
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)\r
+{\r
+ yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);\r
+\r
+ if(cache)\r
+ {\r
+ cache->object = NULL;\r
+ }\r
+}\r
+\r
+\r
+// Invalidate all the cache pages associated with this object\r
+// Do this whenever ther file is deleted or resized.\r
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)\r
+{\r
+ int i;\r
+ yaffs_Device *dev = in->myDev;\r
+ \r
+ // Now invalidate it.\r
+ for(i = 0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ if(dev->srCache[i].object == in)\r
+ {\r
+ dev->srCache[i].object = NULL;\r
+ }\r
+ }\r
}\r
\r
\r
+#else\r
+\r
+static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)\r
+{\r
+ return NULL; \r
+}\r
+\r
+\r
+static yaffs_ChunkCache *yaffs_FindChunkCache(yaffs_Device *dev, int objectId, int chunkId)\r
+{\r
+ return NULL;\r
+}\r
+\r
+static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache)\r
+{\r
+}\r
+\r
+static void yaffs_InvalidateChunkCache(yaffs_Object *obj, int chunkId)\r
+{\r
+}\r
+\r
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)\r
+{\r
+}\r
+\r
+\r
+\r
+#endif\r
+\r
+\r
\r
///////////////////////// File read/write ///////////////////////////////\r
// Read and write have very similar structures.\r
int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)\r
{\r
\r
-// yaffs_Device *dev = in->myDev;\r
- \r
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
\r
int chunk;\r
int start;\r
if(nToCopy != YAFFS_BYTES_PER_CHUNK)\r
{\r
// An incomplete start or end chunk (or maybe both start and end chunk)\r
+#ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
+ yaffs_ChunkCache *cache;\r
+ // If we can't find the data in the cache, then load it up.\r
+ cache = yaffs_FindChunkCache(in,chunk);\r
+ if(!cache)\r
+ {\r
+ cache = yaffs_GrabChunkCache(in->myDev);\r
+ cache->object = in;\r
+ cache->chunkId = chunk;\r
+ cache->dirty = 0;\r
+ yaffs_ReadChunkDataFromObject(in,chunk,cache->data); \r
+ }\r
+ \r
+ yaffs_UseChunkCache(in->myDev,cache,0);\r
+\r
+ memcpy(buffer,&cache->data[start],nToCopy);\r
+#else\r
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
// Read into the local buffer then copy...\r
- \r
yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); \r
memcpy(buffer,&localBuffer[start],nToCopy);\r
+#endif\r
}\r
else\r
{\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
+ \r
+ // Under WinCE can't do direct transfer. Need to use a local buffer.\r
+ // This is because we otherwise screw up WinCE's memory mapper\r
+ yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);\r
+ memcpy(buffer,localBuffer,YAFFS_BYTES_PER_CHUNK);\r
+#else\r
// A full chunk. Read directly into the supplied buffer.\r
yaffs_ReadChunkDataFromObject(in,chunk,buffer);\r
+#endif\r
}\r
\r
n -= nToCopy;\r
}\r
\r
\r
+\r
int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)\r
{ \r
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
\r
int chunk;\r
int start;\r
int n = nBytes;\r
int nDone = 0;\r
int nToWriteBack;\r
- int endOfWrite = offset+nBytes;\r
+ int startOfWrite = offset;\r
int chunkWritten = 0;\r
+ int nBytesRead;\r
+ \r
+ \r
\r
while(n > 0 && chunkWritten >= 0)\r
{\r
\r
// OK now check for the curveball where the start and end are in\r
// the same chunk.\r
+ \r
if( (start + n) < YAFFS_BYTES_PER_CHUNK)\r
{\r
nToCopy = n;\r
- nToWriteBack = (start + n);\r
+ \r
+ // Now folks, to calculate how many bytes to write back....\r
+ // If we're overwriting and not writing to then end of file then\r
+ // we need to write back as much as was there before.\r
+ \r
+ nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * YAFFS_BYTES_PER_CHUNK);\r
+ \r
+ if(nBytesRead > YAFFS_BYTES_PER_CHUNK)\r
+ {\r
+ nBytesRead = YAFFS_BYTES_PER_CHUNK;\r
+ }\r
+ \r
+ nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n);\r
+ \r
}\r
else\r
{\r
if(nToCopy != YAFFS_BYTES_PER_CHUNK)\r
{\r
// An incomplete start or end chunk (or maybe both start and end chunk)\r
+#ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
+ yaffs_ChunkCache *cache;\r
+ // If we can't find the data in the cache, then load it up.\r
+ cache = yaffs_FindChunkCache(in,chunk);\r
+ if(!cache && yaffs_CheckSpaceForChunkCache(in->myDev))\r
+ {\r
+ cache = yaffs_GrabChunkCache(in->myDev);\r
+ cache->object = in;\r
+ cache->chunkId = chunk;\r
+ cache->dirty = 0;\r
+ yaffs_ReadChunkDataFromObject(in,chunk,cache->data); \r
+ }\r
+ \r
+ if(cache)\r
+ { \r
+ yaffs_UseChunkCache(in->myDev,cache,1);\r
+ memcpy(&cache->data[start],buffer,nToCopy);\r
+ }\r
+ else\r
+ {\r
+ chunkWritten = -1; // fail the write\r
+ }\r
+#else\r
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
+\r
+ // An incomplete start or end chunk (or maybe both start and end chunk)\r
// Read into the local buffer then copy, then copy over and write back.\r
\r
yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);\r
\r
chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);\r
\r
- //T(("Write with readback to chunk %d %d\n",chunk,chunkWritten));\r
+ //T(("Write with readback to chunk %d %d start %d copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));\r
+#endif\r
\r
}\r
else\r
{\r
+ \r
+#ifdef CONFIG_YAFFS_WINCE\r
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
+ // Under WinCE can't do direct transfer. Need to use a local buffer.\r
+ // This is because we otherwise screw up WinCE's memory mapper\r
+ memcpy(localBuffer,buffer,YAFFS_BYTES_PER_CHUNK);\r
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,YAFFS_BYTES_PER_CHUNK,0);\r
+#else\r
// A full chunk. Write directly from the supplied buffer.\r
chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);\r
+#endif\r
+ // Since we've overwritten the cached data, we better invalidate it.\r
+ yaffs_InvalidateChunkCache(in,chunk);\r
//T(("Write to chunk %d %d\n",chunk,chunkWritten));\r
}\r
\r
\r
// Update file object\r
\r
- if(endOfWrite > in->variant.fileVariant.fileSize)\r
+ if((startOfWrite + nDone) > in->variant.fileVariant.fileSize)\r
{\r
- in->variant.fileVariant.fileSize = endOfWrite;\r
+ in->variant.fileVariant.fileSize = (startOfWrite + nDone);\r
}\r
\r
in->dirty = 1;\r
- /*in->st_mtime = CURRENT_TIME; only update in flush*/\r
\r
return nDone;\r
}\r
yaffs_Device *dev = in->myDev;\r
\r
__u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
+\r
+ yaffs_FlushFilesChunkCache(in); \r
+ yaffs_InvalidateWholeChunkCache(in);\r
\r
if(in->variantType != YAFFS_OBJECT_TYPE_FILE)\r
{\r
if(in->dirty)\r
{\r
//T(("flushing object header\n"));\r
+ \r
+ yaffs_FlushFilesChunkCache(in);\r
\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ yfsd_WinFileTimeNow(in->win_mtime);\r
+#else\r
in->st_mtime = CURRENT_TIME;\r
+#endif\r
\r
- retVal = yaffs_UpdateObjectHeader(in,NULL);\r
+ retVal = yaffs_UpdateObjectHeader(in,NULL,0);\r
}\r
else\r
{\r
\r
static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)\r
{\r
+\r
+ // First off, invalidate the file's data in the cache, without flushing.\r
+ yaffs_InvalidateWholeChunkCache(in);\r
+ \r
yaffs_RemoveObjectFromDirectory(in);\r
yaffs_DeleteChunk(in->myDev,in->chunkId);\r
+#ifdef __KERNEL__\r
+ if(in->myInode)\r
+ {\r
+ in->myInode->u.generic_ip = NULL;\r
+ in->myInode = 0;\r
+ }\r
+#endif\r
yaffs_FreeObject(in);\r
return YAFFS_OK;\r
\r
// yaffs_DeleteFile deletes the whole file data\r
// and the inode associated with the file.\r
// It does not delete the links associated with the file.\r
-static int yaffs_DeleteFile(yaffs_Object *in)\r
+static int yaffs_UnlinkFile(yaffs_Object *in)\r
{\r
+\r
+#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION\r
// Delete the file data & tnodes\r
-#if 0\r
- yaffs_ResizeFile(in,0);\r
-#else\r
- yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0);\r
\r
-#endif\r
+ yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);\r
+ \r
\r
yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);\r
\r
return yaffs_DoGenericObjectDeletion(in);\r
+#else\r
+ int retVal;\r
+ int immediateDeletion=0;\r
+ retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);\r
+ if(retVal == YAFFS_OK)\r
+ {\r
+ //in->unlinked = 1;\r
+ //in->myDev->nUnlinkedFiles++;\r
+ //in->renameAllowed = 0;\r
+#ifdef __KERNEL__\r
+ if(in->myInode)\r
+ {\r
+ immediateDeletion = 1;\r
+\r
+ }\r
+#endif\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ if(in->inUse <= 0)\r
+ {\r
+ immediateDeletion = 1;\r
+\r
+ }\r
+#endif\r
+ \r
+ if(immediateDeletion)\r
+ {\r
+ T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));\r
+ in->deleted=1;\r
+ in->myDev->nDeletedFiles++;\r
+ }\r
+ \r
+ }\r
+ return retVal;\r
+\r
+ \r
+#endif\r
+}\r
+\r
+int yaffs_DeleteFile(yaffs_Object *in)\r
+{\r
+ int retVal = YAFFS_OK;\r
+ if(!in->unlinked)\r
+ {\r
+ retVal = yaffs_UnlinkFile(in);\r
+ }\r
+ if(retVal == YAFFS_OK && \r
+ in->unlinked &&\r
+ !in->deleted)\r
+ {\r
+ in->deleted = 1;\r
+ in->myDev->nDeletedFiles++;\r
+ }\r
+ return in->deleted ? YAFFS_OK : YAFFS_FAIL; \r
}\r
\r
static int yaffs_DeleteDirectory(yaffs_Object *in)\r
}\r
else if(!list_empty(&obj->hardLinks))\r
{ \r
-#if 0\r
- // Curve ball: We're unlinking an object that has a hardlink.\r
- // Therefore we can't really delete the object.\r
- // Instead, we do the following:\r
- // - Select a hardlink.\r
- // - Re-type a hardlink as the equivalent object and populate the fields, including the\r
- // objectId. Updating the object id is important so that all the hardlinks do not need\r
- // to be rewritten.\r
- // - Update the equivalet object pointers.\r
- // - Delete all object.\r
-\r
- yaffs_Object *hl;\r
- struct list_head *i;\r
-\r
-\r
- yaffs_RemoveObjectFromDirectory(obj);\r
-\r
-\r
-\r
- hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks);\r
- \r
- hl->dirty = 1;\r
- hl->st_mode = obj->st_mode;\r
- hl->st_uid = obj->st_uid;\r
- hl->st_gid = obj->st_gid;\r
- hl->st_atime = obj->st_atime;\r
- hl->st_mtime = obj->st_mtime;\r
- hl->st_ctime = obj->st_ctime;\r
- hl->st_rdev = obj->st_rdev;\r
- \r
- hl->variantType = obj->variantType;\r
- \r
- switch(hl->variantType)\r
- {\r
- case YAFFS_OBJECT_TYPE_FILE:\r
- case YAFFS_OBJECT_TYPE_SYMLINK:\r
- case YAFFS_OBJECT_TYPE_SPECIAL:\r
- // These types are OK to just copy across.\r
- hl->variant = obj->variant;\r
- break;\r
- case YAFFS_OBJECT_TYPE_DIRECTORY:\r
- // Fix the list up\r
- list_add(&hl->variant.directoryVariant.children,\r
- &obj->variant.directoryVariant.children);\r
- list_del(&obj->variant.directoryVariant.children);\r
- \r
- // Now change all the directory children to point to the new parent.\r
- list_for_each(i,&hl->variant.directoryVariant.children)\r
- {\r
- list_entry(i,yaffs_Object,siblings)->parent = hl;\r
- }\r
- break;\r
- \r
- case YAFFS_OBJECT_TYPE_HARDLINK:\r
- case YAFFS_OBJECT_TYPE_UNKNOWN:\r
- // Should not be either of these types.\r
- }\r
- \r
- // Now fix up the hardlink chain\r
- list_del(&obj->hardLinks);\r
-\r
- list_for_each(i,&hl->hardLinks)\r
- {\r
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl;\r
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId;\r
- }\r
- \r
- // Now fix up the hash links.\r
- yaffs_UnhashObject(hl);\r
- hl->objectId = obj->objectId;\r
- yaffs_HashObject(hl);\r
- \r
- // Update the hardlink which has become an object\r
- yaffs_UpdateObjectHeader(hl,NULL);\r
-\r
- // Finally throw away the deleted object\r
- yaffs_DeleteChunk(obj->myDev,obj->chunkId);\r
- yaffs_FreeObject(obj);\r
- \r
- return YAFFS_OK; \r
-#else\r
// Curve ball: We're unlinking an object that has a hardlink.\r
//\r
// This problem arises because we are not strictly following\r
\r
yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);\r
\r
- retVal = yaffs_ChangeObjectName(obj, hl->parent, name);\r
+ retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0);\r
\r
if(retVal == YAFFS_OK)\r
{\r
retVal = yaffs_DoGenericObjectDeletion(hl);\r
}\r
return retVal;\r
-\r
-#endif\r
-\r
\r
}\r
else\r
switch(obj->variantType)\r
{\r
case YAFFS_OBJECT_TYPE_FILE:\r
- return yaffs_DeleteFile(obj);\r
+ return yaffs_UnlinkFile(obj);\r
break;\r
case YAFFS_OBJECT_TYPE_DIRECTORY:\r
return yaffs_DeleteDirectory(obj);\r
yaffs_BlockState state;\r
yaffs_Object *hardList = NULL;\r
yaffs_Object *hl;\r
- \r
+ yaffs_BlockInfo *bi;\r
\r
\r
yaffs_ObjectHeader *oh;\r
for(blk = dev->startBlock; blk <= dev->endBlock; blk++)\r
{\r
deleted = 0;\r
- dev->blockInfo[blk].pageBits = 0;\r
- dev->blockInfo[blk].pagesInUse = 0;\r
+ bi = yaffs_GetBlockInfo(dev,blk);\r
+ bi->pageBits = 0;\r
+ bi->pagesInUse = 0;\r
state = YAFFS_BLOCK_STATE_SCANNING;\r
\r
\r
if(yaffs_IsBlockBad(dev,blk))\r
{\r
state = YAFFS_BLOCK_STATE_DEAD;\r
- T((TSTR("block %d is bad" TENDSTR),blk));\r
+ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));\r
}\r
\r
// Read each chunk in the block.\r
\r
\r
// This block looks ok, now what's in this chunk?\r
- yaffs_GetTagsFromSpare(&spare,&tags);\r
+ yaffs_GetTagsFromSpare(dev,&spare,&tags);\r
\r
if(yaffs_CountBits(spare.pageStatus) < 6)\r
{\r
else\r
{\r
// this is the block being allocated from\r
- T((TSTR(" Allocating from %d %d" TENDSTR),blk,c));\r
+ T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));\r
state = YAFFS_BLOCK_STATE_ALLOCATING;\r
dev->allocationBlock = blk;\r
dev->allocationPage = c;\r
{\r
int endpos;\r
// A data chunk.\r
- dev->blockInfo[blk].pageBits |= (1 << c);\r
- dev->blockInfo[blk].pagesInUse++;\r
+ bi->pageBits |= (1 << c);\r
+ bi->pagesInUse++;\r
\r
in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);\r
// PutChunkIntoFIle checks for a clash (two data chunks with\r
if(in->variant.fileVariant.scannedFileSize <endpos)\r
{\r
in->variant.fileVariant.scannedFileSize = endpos;\r
+#ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE\r
+ in->variant.fileVariant.fileSize = \r
+ in->variant.fileVariant.scannedFileSize;\r
+#endif\r
+\r
}\r
//T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); \r
}\r
{\r
// chunkId == 0, so it is an ObjectHeader.\r
// Thus, we read in the object header and make the object\r
- dev->blockInfo[blk].pageBits |= (1 << c);\r
- dev->blockInfo[blk].pagesInUse++;\r
+ bi->pageBits |= (1 << c);\r
+ bi->pagesInUse++;\r
\r
yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1);\r
\r
}\r
}\r
\r
- if(!in->valid)\r
+ if(!in->valid &&\r
+ (tags.objectId == YAFFS_OBJECTID_ROOT ||\r
+ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))\r
+ {\r
+ // We only load some info, don't fiddle with directory structure\r
+ in->valid = 1;\r
+ in->variantType = oh->type;\r
+ \r
+ in->st_mode = oh->st_mode;\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ in->win_atime[0] = oh->win_atime[0];\r
+ in->win_ctime[0] = oh->win_ctime[0];\r
+ in->win_mtime[0] = oh->win_mtime[0];\r
+ in->win_atime[1] = oh->win_atime[1];\r
+ in->win_ctime[1] = oh->win_ctime[1];\r
+ in->win_mtime[1] = oh->win_mtime[1];\r
+#else\r
+ in->st_uid = oh->st_uid;\r
+ in->st_gid = oh->st_gid;\r
+ in->st_atime = oh->st_atime;\r
+ in->st_mtime = oh->st_mtime;\r
+ in->st_ctime = oh->st_ctime;\r
+ in->st_rdev = oh->st_rdev;\r
+#endif\r
+ in->chunkId = chunk;\r
+\r
+ }\r
+ else if(!in->valid)\r
{\r
// we need to load this info\r
\r
in->variantType = oh->type;\r
\r
in->st_mode = oh->st_mode;\r
+#ifdef CONFIG_YAFFS_WINCE\r
+ in->win_atime[0] = oh->win_atime[0];\r
+ in->win_ctime[0] = oh->win_ctime[0];\r
+ in->win_mtime[0] = oh->win_mtime[0];\r
+ in->win_atime[1] = oh->win_atime[1];\r
+ in->win_ctime[1] = oh->win_ctime[1];\r
+ in->win_mtime[1] = oh->win_mtime[1];\r
+#else\r
in->st_uid = oh->st_uid;\r
in->st_gid = oh->st_gid;\r
in->st_atime = oh->st_atime;\r
in->st_mtime = oh->st_mtime;\r
in->st_ctime = oh->st_ctime;\r
in->st_rdev = oh->st_rdev;\r
+#endif\r
in->chunkId = chunk;\r
\r
- in->sum = oh->sum;\r
+ yaffs_SetObjectName(in,oh->name);\r
in->dirty = 0;\r
\r
// directory stuff...\r
// Hoosterman, another problem....\r
// We're trying to use a non-directory as a directory\r
// Todo ... handle\r
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tradgedy: attempting to use non-directory as a directory in scan" TENDSTR)));\r
+\r
}\r
\r
- yaffs_AddObjectToDirectory(parent,in); \r
+ yaffs_AddObjectToDirectory(parent,in);\r
+ if(parent == dev->unlinkedDir)\r
+ {\r
+ in->deleted = 1; // If it is unlinked at start up then it wants deleting\r
+ dev->nDeletedFiles++;\r
+ }\r
\r
// Note re hardlinks.\r
// Since we might scan a hardlink before its equivalent object is scanned\r
case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem\r
break;\r
case YAFFS_OBJECT_TYPE_FILE:\r
+#ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE\r
in->variant.fileVariant.fileSize = oh->fileSize;\r
+#endif\r
break;\r
case YAFFS_OBJECT_TYPE_HARDLINK:\r
in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;\r
state = YAFFS_BLOCK_STATE_FULL; \r
}\r
\r
- dev->blockInfo[blk].blockState = state;\r
+ bi->blockState = state;\r
\r
// Now let's see if it was dirty\r
- if( dev->blockInfo[blk].pagesInUse == 0 &&\r
- dev->blockInfo[blk].blockState == YAFFS_BLOCK_STATE_FULL)\r
+ if( bi->pagesInUse == 0 &&\r
+ bi->blockState == YAFFS_BLOCK_STATE_FULL)\r
{\r
yaffs_BlockBecameDirty(dev,blk);\r
}\r
// Now add it\r
list_add(&obj->siblings,&directory->variant.directoryVariant.children);\r
obj->parent = directory;\r
+ \r
+ if(directory == obj->myDev->unlinkedDir)\r
+ {\r
+ obj->unlinked = 1;\r
+ obj->myDev->nUnlinkedFiles++;\r
+ obj->renameAllowed = 0;\r
+ }\r
}\r
\r
static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)\r
strncpy(name,locName,buffSize - 1);\r
\r
}\r
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM\r
+ else if(obj->shortName[0])\r
+ {\r
+ strcpy(name,obj->shortName);\r
+ }\r
+#endif\r
else\r
{\r
__u8 buffer[YAFFS_BYTES_PER_CHUNK];\r
\r
int yaffs_GetObjectLinkCount(yaffs_Object *obj)\r
{\r
- int count = 1; // the object itself\r
+ int count = 0; \r
struct list_head *i;\r
\r
+ if(!obj->unlinked)\r
+ {\r
+ count++; // the object itself\r
+ }\r
list_for_each(i,&obj->hardLinks)\r
{\r
- count++;\r
+ count++; // add the hard links;\r
}\r
return count;\r
\r
}\r
}\r
\r
+#ifndef CONFIG_YAFFS_WINCE\r
\r
int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)\r
{\r
\r
if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);\r
\r
- yaffs_UpdateObjectHeader(obj,NULL);\r
+ yaffs_UpdateObjectHeader(obj,NULL,1);\r
\r
return YAFFS_OK;\r
\r
\r
}\r
\r
-\r
+#endif\r
\r
int yaffs_DumpObject(yaffs_Object *obj)\r
{\r
\r
yaffs_GetObjectName(obj,name,256);\r
\r
- YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n",\r
+ T(YAFFS_TRACE_ALWAYS,(TSTR("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n" TENDSTR),\r
obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial, \r
obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));\r
\r
\r
int yaffs_GutsInitialise(yaffs_Device *dev)\r
{\r
- unsigned nChunks,x;\r
+ unsigned x;\r
int bits;\r
+ int extraBits;\r
+ int nBlocks;\r
\r
\r
\r
if(!yaffs_CheckStructures())\r
{\r
+ T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR)));\r
return YAFFS_FAIL;\r
}\r
+ \r
+ if(dev->startBlock <= 0 ||\r
+ (dev->endBlock - dev->startBlock) < 10)\r
+ {\r
+ T(YAFFS_TRACE_ALWAYS,(TSTR("startBlock %d or endBlock %d invalid\n" TENDSTR),\r
+ dev->startBlock, dev->endBlock));\r
+ return YAFFS_FAIL;\r
+ }\r
+ \r
+ nBlocks = dev->endBlock - dev->startBlock + 1;\r
+ \r
\r
\r
// OK now calculate a few things for the device\r
// Calculate chunkGroupBits. \r
- // If there are 64k or less chunks then this is 1\r
- // Else it is log2(nChunks) - 16\r
- //\r
- x = nChunks = YAFFS_CHUNKS_PER_BLOCK * dev->nBlocks;\r
+ // We need to find the next power of 2 > than endBlock\r
+ \r
+ x = YAFFS_CHUNKS_PER_BLOCK * (dev->endBlock+1);\r
\r
- for(bits = 0, x = nChunks; (x & 1) == 0; bits++)\r
+ for(bits = extraBits = 0; x > 1; bits++)\r
{\r
+ if(x & 1) extraBits++;\r
x >>= 1;\r
}\r
+\r
+ if(extraBits > 0) bits++;\r
\r
- if( x != 1)\r
- {\r
- // Not a power of 2\r
- YPRINTF(("nBlocks should be a power of 2 but is %u\n",\r
- dev->nBlocks));\r
- return YAFFS_FAIL;\r
- }\r
\r
+ // Level0 Tnodes are 16 bits, so if the bitwidth of the\r
+ // chunk range we're using is greater than 16 we need \r
+ // to figure out chunk shift and chunkGroupSize\r
if(bits <= 16) \r
{\r
dev->chunkGroupBits = 0;\r
- dev->chunkGroupSize = 1;\r
}\r
else\r
{\r
dev->chunkGroupBits = bits - 16;\r
- dev->chunkGroupSize = nChunks>> 16;\r
}\r
+ dev->chunkGroupSize = 1 << dev->chunkGroupBits;\r
+ \r
\r
// More device initialisation\r
dev->garbageCollectionRequired = 0;\r
+ dev->garbageCollections = 0;\r
dev->currentDirtyChecker = 0;\r
dev->bufferedBlock = -1;\r
dev->doingBufferedBlockRewrite = 0;\r
dev->blockSelectedForGC = -1;\r
+ dev->nDeletedFiles = 0;\r
+ dev->nBackgroundDeletions=0;\r
+ dev->nUnlinkedFiles = 0;\r
+ dev->eccFixed=0;\r
+ dev->eccUnfixed=0;\r
+ dev->tagsEccFixed=0;\r
+ dev->tagsEccUnfixed=0;\r
\r
- yaffs_InitialiseBlocks(dev);\r
+ yaffs_InitialiseBlocks(dev,nBlocks);\r
\r
yaffs_InitialiseTnodes(dev);\r
\r
yaffs_InitialiseObjects(dev);\r
\r
+#ifdef CONFIG_YAFFS_SHORT_OP_CACHE\r
+ { \r
+ int i;\r
+ for(i=0; i < YAFFS_N_CACHE_CHUNKS; i++)\r
+ {\r
+ dev->srCache[i].object = NULL;\r
+ dev->srCache[i].lastUse = 0;\r
+ dev->srCache[i].dirty = 0;\r
+ }\r
+ dev->srLastUse = 0;\r
+ }\r
+#endif\r
+\r
+ dev->cacheHits = 0;\r
\r
- // Initialise the root and lost and found directories\r
- dev->lostNFoundDir = dev->rootDir = NULL;\r
+ \r
+ // Initialise the unlinked, root and lost and found directories\r
+ dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = NULL;\r
+ \r
+ dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR);\r
+\r
dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);\r
- dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_ROOT_MODE | S_IFDIR);\r
+ dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR);\r
yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);\r
+ \r
\r
// Now scan the flash. \r
yaffs_Scan(dev);\r
int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)\r
{\r
int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);\r
+ \r
+ struct list_head *i; \r
+ yaffs_Object *l;\r
+ \r
+ \r
+ // To the free chunks add the chunks that are in the deleted unlinked files.\r
+ list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)\r
+ {\r
+ l = list_entry(i, yaffs_Object,siblings);\r
+ if(l->deleted)\r
+ {\r
+ nFree++;\r
+ nFree += l->nDataChunks;\r
+ }\r
+ }\r
+\r
\r
return (nFree < 0) ? 0 : nFree; \r
\r
\r
#define yaffs_CheckStruct(structure,syze, name) \\r
if(sizeof(structure) != syze) \\r
- { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \\r
+ { \\r
+ T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),name,syze,sizeof(structure))); \\r
return YAFFS_FAIL; \\r
}\r
\r
return YAFFS_OK;\r
}\r
\r
+#if 0\r
void yaffs_GutsTest(yaffs_Device *dev)\r
{\r
\r
if(yaffs_CheckStructures() != YAFFS_OK)\r
{\r
- YPRINTF(("One or more structures malformed-- aborting\n"));\r
+ T(YAFFS_TRACE_ALWAYS,(TSTR("One or more structures malformed-- aborting\n" TENDSTR)));\r
return;\r
}\r
- else\r
- {\r
- YPRINTF(("Structures OK\n"));\r
- }\r
\r
yaffs_TnodeTest(dev);\r
yaffs_ObjectTest(dev); \r
}\r
+#endif\r
+\r
\r
\r