-/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
- * yaffs_guts.c The main guts of YAFFS
- *
- * 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
-
-#include "yportenv.h"
-
-#include "yaffsinterface.h"
-#include "yaffs_guts.h"
-
-
-#if 0
-#define T(x) printf x
-#else
-#define T(x)
-#endif
-
-// External functions for ECC on data
-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
-
-
-// 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.
-
-static const char yaffs_countBits[256] =
-{
-0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
-4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
-};
-
-
-
-// Device info
-//static yaffs_Device *yaffs_device;
-//yaffs_Object *yaffs_rootDir;
-//yaffs_Object *yaffs_lostNFound;
-
-
-
-// Local prototypes
-static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
-static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
-static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
-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 char *name);
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);
-static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
-static int yaffs_CheckStructures(void);
-
-loff_t yaffs_GetFileSize(yaffs_Object *obj);
-
-
-static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
-
-#ifdef YAFFS_PARANOID
-static int yaffs_CheckFileSanity(yaffs_Object *in);
-#else
-#define yaffs_CheckFileSanity(in)
-#endif
-
-static int __inline__ yaffs_HashFunction(int n)
-{
- return (n % YAFFS_NOBJECT_BUCKETS);
-}
-
-
-yaffs_Object *yaffs_Root(yaffs_Device *dev)
-{
- return dev->rootDir;
-}
-
-yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
-{
- return dev->lostNFoundDir;
-}
-
-
-static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
-{
- return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
-}
-
-int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
-{
- int retVal;
- __u8 calcEcc[3];
- yaffs_Spare localSpare;
-
- 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;
- }
-
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
- if(data)
- {
- // Do ECC correction
- //Todo handle any errors
- nand_calculate_ecc(data,calcEcc);
- nand_correct_data (data,spare->ecc1, calcEcc);
- nand_calculate_ecc(&data[256],calcEcc);
- nand_correct_data (&data[256],spare->ecc2, calcEcc);
- }
- return retVal;
-}
-
-#ifdef YAFFS_PARANOID
-
-static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
-{
- static int init = 0;
- static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
- static __u8 data[YAFFS_BYTES_PER_CHUNK];
- static __u8 spare[16];
-
- int retVal;
-
- retVal = YAFFS_OK;
-
- dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
-
-
-
- if(!init)
- {
- memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);
- init = 1;
- }
-
- if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) retVal = YAFFS_FAIL;
- if(memcmp(cmpbuf,spare,16)) retVal = YAFFS_FAIL;
-
- return retVal;
-
-}
-
-#endif
-
-
-int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
-{
- return dev->eraseBlockInNAND(dev,blockInNAND);
-}
-
-int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
-{
- return dev->initialiseNAND(dev);
-}
-
-static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)
-{
- int chunk;
-
- int writeOk = 0;
-
- unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
- yaffs_Spare rbSpare;
-
- do{
- chunk = yaffs_AllocateChunk(dev,useReserve);
-
- if(chunk >= 0)
- {
- writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare);
- if(writeOk)
- {
- // Readback & verify
- // If verify fails, then delete this chunk and try again
- // To verify we compare everything except the block and
- // page status bytes.
- yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare);
-
- if(memcmp(data,rbData,YAFFS_BYTES_PER_CHUNK) != 0 ||
- spare->tagByte0 != rbSpare.tagByte0 ||
- spare->tagByte1 != rbSpare.tagByte1 ||
- spare->tagByte2 != rbSpare.tagByte2 ||
- spare->tagByte3 != rbSpare.tagByte3 ||
- spare->tagByte4 != rbSpare.tagByte4 ||
- spare->tagByte5 != rbSpare.tagByte5 ||
- spare->tagByte6 != rbSpare.tagByte6 ||
- spare->tagByte7 != rbSpare.tagByte7 ||
- spare->ecc1[0] != rbSpare.ecc1[0] ||
- spare->ecc1[1] != rbSpare.ecc1[1] ||
- spare->ecc1[2] != rbSpare.ecc1[2] ||
- spare->ecc2[0] != rbSpare.ecc2[0] ||
- spare->ecc2[1] != rbSpare.ecc2[1] ||
- spare->ecc2[2] != rbSpare.ecc2[2] )
- {
- // Didn't verify
- yaffs_DeleteChunk(dev,chunk);
- writeOk = 0;
- }
-
- }
- }
- } while(chunk >= 0 && ! writeOk);
-
- return chunk;
-}
-
-
-
-
-///////////////////////// Object management //////////////////
-// List of spare objects
-// The list is hooked together using the first pointer
-// in the object
-
-// static yaffs_Object *yaffs_freeObjects = NULL;
-
-// static int yaffs_nFreeObjects;
-
-// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;
-
-// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];
-
-
-static __u16 yaffs_CalcNameSum(const char *name)
-{
- __u16 sum = 0;
- __u16 i = 1;
-
- __u8 *bname = (__u8 *)name;
-
- while (*bname)
- {
- sum += (*bname) * i;
- i++;
- bname++;
- }
- return sum;
-}
-
-
-void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
-{
- nand_calculate_ecc (data , spare->ecc1);
- nand_calculate_ecc (&data[256] , spare->ecc2);
-}
-
-void yaffs_CalcTagsECC(yaffs_Tags *tags)
-{
- // Todo don't do anything yet. Need to calculate ecc
- unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
- unsigned i,j;
- unsigned ecc = 0;
- unsigned bit = 0;
-
- tags->ecc = 0;
-
- for(i = 0; i < 8; i++)
- {
- for(j = 1; j &0x7f; j<<=1)
- {
- bit++;
- if(b[i] & j)
- {
- ecc ^= bit;
- }
- }
- }
-
- tags->ecc = ecc;
-
-
-}
-
-void yaffs_CheckECCOnTags(yaffs_Tags *tags)
-{
- unsigned ecc = tags->ecc;
-
- yaffs_CalcTagsECC(tags);
-
- ecc ^= tags->ecc;
-
- if(ecc)
- {
- // Needs fixing
- unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
-
- ecc--;
-
- b[ecc / 8] ^= (1 << (ecc & 7));
-
- // Now recvalc the ecc
- yaffs_CalcTagsECC(tags);
- }
-}
-
-
-///////////////////////// TNODES ///////////////////////
-
-// List of spare tnodes
-// The list is hooked together using the first pointer
-// in the tnode.
-
-//static yaffs_Tnode *yaffs_freeTnodes = NULL;
-
-// static int yaffs_nFreeTnodes;
-
-//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;
-
-
-
-// yaffs_CreateTnodes creates a bunch more tnodes and
-// adds them to the tnode free list.
-// Don't use this function directly
-
-static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)
-{
- int i;
- yaffs_Tnode *newTnodes;
- yaffs_TnodeList *tnl;
-
- if(nTnodes < 1) return YAFFS_OK;
-
- // make these things
-
- newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));
-
- if (!newTnodes)
- {
- YALERT("Could not malloc tnodes");
- return YAFFS_FAIL;
- }
-
- // Hook them into the free list
- for(i = 0; i < nTnodes - 1; i++)
- {
- newTnodes[i].internal[0] = &newTnodes[i+1];
- }
-
- newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
- dev->freeTnodes = newTnodes;
- dev->nFreeTnodes+= nTnodes;
- dev->nTnodesCreated += nTnodes;
-
- // Now add this bunch of tnodes to a list for freeing up.
-
- tnl = YMALLOC(sizeof(yaffs_TnodeList));
- if(!tnl)
- {
- YALERT("Could not add tnodes to management list");
- }
- else
- {
- tnl->tnodes = newTnodes;
- tnl->next = dev->allocatedTnodeList;
- dev->allocatedTnodeList = tnl;
- }
-
-
- YINFO("Tnodes created");
-
-
- return YAFFS_OK;
-}
-
-
-// GetTnode gets us a clean tnode. Tries to make allocate more if we run out
-static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
-{
- yaffs_Tnode *tn = NULL;
-
- // If there are none left make more
- if(!dev->freeTnodes)
- {
- yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);
- }
-
- if(dev->freeTnodes)
- {
- tn = dev->freeTnodes;
- dev->freeTnodes = dev->freeTnodes->internal[0];
- dev->nFreeTnodes--;
- // zero out
- memset(tn,0,sizeof(yaffs_Tnode));
- }
-
-
- return tn;
-}
-
-
-// FreeTnode frees up a tnode and puts it back on the free list
-static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
-{
- tn->internal[0] = dev->freeTnodes;
- dev->freeTnodes = tn;
- dev->nFreeTnodes++;
-}
-
-
-static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
-{
- // Free the list of allocated tnodes
-
- while(dev->allocatedTnodeList)
- {
- YFREE(dev->allocatedTnodeList->tnodes);
- dev->allocatedTnodeList = dev->allocatedTnodeList->next;
- }
-
- dev->freeTnodes = NULL;
- dev->nFreeTnodes = 0;
-}
-
-static void yaffs_InitialiseTnodes(yaffs_Device*dev)
-{
- dev->allocatedTnodeList = NULL;
- dev->freeTnodes = NULL;
- dev->nFreeTnodes = 0;
- dev->nTnodesCreated = 0;
-
-}
-
-void yaffs_TnodeTest(yaffs_Device *dev)
-{
-
- int i;
- int j;
- yaffs_Tnode *tn[1000];
-
- YINFO("Testing TNodes");
-
- for(j = 0; j < 50; j++)
- {
- for(i = 0; i < 1000; i++)
- {
- tn[i] = yaffs_GetTnode(dev);
- if(!tn[i])
- {
- YALERT("Getting tnode failed");
- }
- }
- for(i = 0; i < 1000; i+=3)
- {
- yaffs_FreeTnode(dev,tn[i]);
- tn[i] = NULL;
- }
-
- }
-}
-
-////////////////// END OF TNODE MANIPULATION ///////////////////////////
-
-/////////////// Functions to manipulate the look-up tree (made up of tnodes)
-// The look up tree is represented by the top tnode and the number of topLevel
-// in the tree. 0 means only the level 0 tnode is in the tree.
-
-
-// FindLevel0Tnode finds the level 0 tnode, if one exists.
-// Used when reading.....
-static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)
-{
-
- yaffs_Tnode *tn = fStruct->top;
- __u32 i;
- int requiredTallness;
- int level = fStruct->topLevel;
-
- // Check sane level and chunk Id
- if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
- {
- char str[50];
- sprintf(str,"Bad level %d",level);
- YALERT(str);
- return NULL;
- }
- if(chunkId > YAFFS_MAX_CHUNK_ID)
- {
- char str[50];
- sprintf(str,"Bad chunkId %d",chunkId);
- YALERT(str);
- return NULL;
- }
-
- // First check we're tall enough (ie enough topLevel)
-
- i = chunkId >> (dev->chunkGroupBits + YAFFS_TNODES_LEVEL0_BITS);
- requiredTallness = 0;
- while(i)
- {
- i >>= YAFFS_TNODES_INTERNAL_BITS;
- requiredTallness++;
- }
-
-
- if(requiredTallness > fStruct->topLevel)
- {
- // Not tall enough, so we can't find it, return NULL.
- return NULL;
- }
-
-
- // Traverse down to level 0
- while (level > 0 && tn)
- {
- tn = tn->internal[(chunkId >>(dev->chunkGroupBits + YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) &
- YAFFS_TNODES_INTERNAL_MASK];
- level--;
-
- }
-
- return tn;
-}
-
-// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
-// This happens in two steps:
-// 1. If the tree isn't tall enough, then make it taller.
-// 2. Scan down the tree towards the level 0 tnode adding tnodes if required.
-//
-// Used when modifying the tree.
-//
-static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)
-{
-
- yaffs_Tnode *tn;
-
- int requiredTallness;
-
- __u32 i;
- __u32 l;
-
-
- T(("AddOrFind topLevel=%d, chunk=%d",fStruct->topLevel,chunkId));
-
- // Check sane level and page Id
- if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)
- {
- char str[50];
- sprintf(str,"Bad level %d",fStruct->topLevel);
- YALERT(str);
- return NULL;
- }
-
- if(chunkId > YAFFS_MAX_CHUNK_ID)
- {
- char str[50];
- sprintf(str,"Bad chunkId %d",chunkId);
- YALERT(str);
- return NULL;
- }
-
- // First check we're tall enough (ie enough topLevel)
-
- i = chunkId >> (dev->chunkGroupBits + YAFFS_TNODES_LEVEL0_BITS);
- requiredTallness = 0;
- while(i)
- {
- i >>= YAFFS_TNODES_INTERNAL_BITS;
- requiredTallness++;
- }
-
- T((" required=%d",requiredTallness));
-
-
- if(requiredTallness > fStruct->topLevel)
- {
- // Not tall enough,gotta make the tree taller
- for(i = fStruct->topLevel; i < requiredTallness; i++)
- {
- T((" add new top"));
-
- tn = yaffs_GetTnode(dev);
-
- if(tn)
- {
- tn->internal[0] = fStruct->top;
- fStruct->top = tn;
- }
- else
- {
- YALERT("No more tnodes");
- }
- }
-
- fStruct->topLevel = requiredTallness;
- }
-
-
- // Traverse down to level 0, adding anything we need
-
- l = fStruct->topLevel;
- tn = fStruct->top;
- while (l > 0 && tn)
- {
- i = (chunkId >> (dev->chunkGroupBits +YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) &
- YAFFS_TNODES_INTERNAL_MASK;
-
- T((" [%d:%d]",l,i));
-
- if(!tn->internal[i])
- {
- T((" added"));
-
- tn->internal[i] = yaffs_GetTnode(dev);
- }
-
- tn = tn->internal[i];
- l--;
-
- }
-
- T(("\n"));
-
- return tn;
-}
-
-
-// Pruning removes any part of the file structure tree that is beyond the
-// bounds of the file.
-// A file should only get pruned when its size is reduced.
-//
-// Before pruning, the chunks must be pulled from the tree and the
-// level 0 tnode entries must be zeroed out.
-// Could also use this for file deletion, but that's probably better handled
-// by a special case.
-
-// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()
-
-static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)
-{
- int i;
- int hasData;
-
- if(tn)
- {
- hasData = 0;
-
- for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)
- {
- if(tn->internal[i] && level > 0)
- {
- tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);
- }
-
- if(tn->internal[i])
- {
- hasData++;
- }
- }
-
- if(hasData == 0 && del0)
- {
- // Free and return NULL
-
- yaffs_FreeTnode(dev,tn);
- tn = NULL;
- }
-
- }
-
- return tn;
-
-}
-
-static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)
-{
- int i;
- int hasData;
- int done = 0;
- yaffs_Tnode *tn;
-
- if(fStruct->topLevel > 0)
- {
- fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);
-
- // Now we have a tree with all the non-zero branches NULL but the height
- // is the same as it was.
- // Let's see if we can trim internal tnodes to shorten the tree.
- // We can do this if only the 0th element in the tnode is in use
- // (ie all the non-zero are NULL)
-
- while(fStruct->topLevel && !done)
- {
- tn = fStruct->top;
-
- hasData = 0;
- for(i = 1; i <YAFFS_NTNODES_INTERNAL; i++)
- {
- if(tn->internal[i])
- {
- hasData++;
- }
- }
-
- if(!hasData)
- {
- fStruct->top = tn->internal[0];
- fStruct->topLevel--;
- yaffs_FreeTnode(dev,tn);
- }
- else
- {
- done = 1;
- }
- }
- }
-
- return YAFFS_OK;
-}
-
-
-
-
-/////////////////////// End of File Structure functions. /////////////////
-
-// yaffs_CreateFreeObjects creates a bunch more objects and
-// adds them to the object free list.
-static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
-{
- int i;
- yaffs_Object *newObjects;
- yaffs_ObjectList *list;
-
- if(nObjects < 1) return YAFFS_OK;
-
- // make these things
-
- newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
-
- if (!newObjects)
- {
- YALERT("Could not allocate more objects");
- return YAFFS_FAIL;
- }
-
- // Hook them into the free list
- for(i = 0; i < nObjects - 1; i++)
- {
- (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1];
- }
-
- newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
- dev->freeObjects = newObjects;
- dev->nFreeObjects+= nObjects;
- dev->nObjectsCreated+= nObjects;
-
- // Now add this bunch of Objects to a list for freeing up.
-
- list = YMALLOC(sizeof(yaffs_ObjectList));
- if(!list)
- {
- YALERT("Could not add Objects to management list");
- }
- else
- {
- list->objects = newObjects;
- list->next = dev->allocatedObjectList;
- dev->allocatedObjectList = list;
- }
-
-
- YINFO("Objects created");
-
-
- return YAFFS_OK;
-}
-
-
-// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out
-static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
-{
- yaffs_Object *tn = NULL;
-
- // If there are none left make more
- if(!dev->freeObjects)
- {
- yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);
- }
-
- if(dev->freeObjects)
- {
- tn = dev->freeObjects;
- dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);
- dev->nFreeObjects--;
-
- // Now sweeten it up...
-
- memset(tn,0,sizeof(yaffs_Object));
- tn->chunkId = -1;
- tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
- INIT_LIST_HEAD(&(tn->hardLinks));
- INIT_LIST_HEAD(&(tn->hashLink));
- INIT_LIST_HEAD(&tn->siblings);
-
- // Add it to the lost and found directory.
- // NB Can't put root or lostNFound in lostNFound so
- // check if lostNFound exists first
- if(dev->lostNFoundDir)
- {
- yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn);
- }
- }
-
-
- return tn;
-}
-
-static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)
-{
-
- yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY);
- if(obj)
- {
- obj->fake = 1; // it is fake so it has no NAND presence...
- obj->renameAllowed= 0; // ... and we're not allowed to rename it...
- obj->unlinkAllowed= 0; // ... or unlink it
- obj->st_mode = mode;
- obj->myDev = dev;
- obj->chunkId = 0; // Not a valid chunk.
- }
-
- return obj;
-
-}
-
-
-static void yaffs_UnhashObject(yaffs_Object *tn)
-{
- int bucket;
- yaffs_Device *dev = tn->myDev;
-
-
- // If it is still linked into the bucket list, free from the list
- if(!list_empty(&tn->hashLink))
- {
- list_del_init(&tn->hashLink);
- bucket = yaffs_HashFunction(tn->objectId);
- dev->objectBucket[bucket].count--;
- }
-
-}
-
-
-// FreeObject frees up a Object and puts it back on the free list
-static void yaffs_FreeObject(yaffs_Object *tn)
-{
-
- yaffs_Device *dev = tn->myDev;
-
- yaffs_UnhashObject(tn);
-
- // Link into the free list.
- (yaffs_Object *)(tn->siblings.next) = dev->freeObjects;
- dev->freeObjects = tn;
- dev->nFreeObjects++;
-}
-
-
-
-
-static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
-{
- // Free the list of allocated Objects
-
- while( dev->allocatedObjectList)
- {
- YFREE(dev->allocatedObjectList->objects);
- dev->allocatedObjectList = dev->allocatedObjectList->next;
- }
-
- dev->freeObjects = NULL;
- dev->nFreeObjects = 0;
-}
-
-static void yaffs_InitialiseObjects(yaffs_Device *dev)
-{
- int i;
-
- dev->allocatedObjectList = NULL;
- dev->freeObjects = NULL;
- dev->nFreeObjects = 0;
-
- for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)
- {
- INIT_LIST_HEAD(&dev->objectBucket[i].list);
- dev->objectBucket[i].count = 0;
- }
-
-}
-
-
-
-
-
-
-int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
-{
- static int x = 0;
- int i;
- int l = 999;
- int lowest = 999999;
-
-
- // First let's see if we can find one that's empty.
-
- for(i = 0; i < 10 && lowest > 0; i++)
- {
- x++;
- x %= YAFFS_NOBJECT_BUCKETS;
- if(dev->objectBucket[x].count < lowest)
- {
- lowest = dev->objectBucket[x].count;
- l = x;
- }
-
- }
-
- // If we didn't find an empty list, then try
- // looking a bit further for a short one
-
- for(i = 0; i < 10 && lowest > 3; i++)
- {
- x++;
- x %= YAFFS_NOBJECT_BUCKETS;
- if(dev->objectBucket[x].count < lowest)
- {
- lowest = dev->objectBucket[x].count;
- l = x;
- }
-
- }
-
- return l;
-}
-
-static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)
-{
- int bucket = yaffs_FindNiceObjectBucket(dev);
-
- // Now find an object value that has not already been taken
- // by scanning the list.
-
- int found = 0;
- struct list_head *i;
-
- int n = bucket;
-
- //yaffs_CheckObjectHashSanity();
-
- while(!found)
- {
- found = 1;
- n += YAFFS_NOBJECT_BUCKETS;
- if(1 ||dev->objectBucket[bucket].count > 0)
- {
- list_for_each(i,&dev->objectBucket[bucket].list)
- {
- // If there is already one in the list
- if(list_entry(i, yaffs_Object,hashLink)->objectId == n)
- {
- found = 0;
- }
- }
- }
- }
-
- //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);
-
- return n;
-}
-
-void yaffs_HashObject(yaffs_Object *in)
-{
- int bucket = yaffs_HashFunction(in->objectId);
- yaffs_Device *dev = in->myDev;
-
- if(!list_empty(&in->hashLink))
- {
- YINFO("!!!");
- }
-
-
- list_add(&in->hashLink,&dev->objectBucket[bucket].list);
- dev->objectBucket[bucket].count++;
-
-}
-
-yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,int number)
-{
- int bucket = yaffs_HashFunction(number);
- struct list_head *i;
- yaffs_Object *in;
-
- list_for_each(i,&dev->objectBucket[bucket].list)
- {
- // Lookm if it is in the list
- in = list_entry(i, yaffs_Object,hashLink);
- if(in->objectId == number)
- {
- return in;
- }
- }
-
- return NULL;
-}
-
-
-
-yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)
-{
-
- yaffs_Object *theObject;
-
- if(number < 0)
- {
- number = yaffs_CreateNewObjectNumber(dev);
- }
-
- theObject = yaffs_AllocateEmptyObject(dev);
-
- if(theObject)
- {
- theObject->fake = 0;
- theObject->renameAllowed = 1;
- theObject->unlinkAllowed = 1;
- theObject->objectId = number;
- theObject->myDev = dev;
- yaffs_HashObject(theObject);
- theObject->variantType = type;
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;
-
- switch(type)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- theObject->variant.fileVariant.fileSize = 0;
- theObject->variant.fileVariant.topLevel = 0;
- theObject->variant.fileVariant.top = yaffs_GetTnode(dev);
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- // No action required
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- // No action required
- break;
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- // todo this should not happen
- }
- }
-
- return theObject;
-}
-
-yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
-{
- yaffs_Object *theObject = NULL;
-
- if(number >= 0)
- {
- theObject = yaffs_FindObjectByNumber(dev,number);
- }
-
- if(!theObject)
- {
- theObject = yaffs_CreateNewObject(dev,number,type);
- }
-
- return theObject;
-
-}
-
-char *yaffs_CloneString(const char *str)
-{
- char *newStr = NULL;
-
- if(str && *str)
- {
- newStr = YMALLOC(strlen(str) + 1);
- strcpy(newStr,str);
- }
-
- return newStr;
-
-}
-
-//
-// Mknod (create) a new object.
-// equivalentObject only has meaning for a hard link;
-// aliasString only has meaning for a sumlink.
-yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
- yaffs_Object *parent,
- const char *name,
- __u32 mode,
- __u32 uid,
- __u32 gid,
- yaffs_Object *equivalentObject,
- const char *aliasString)
-{
- yaffs_Object *in;
-
- yaffs_Device *dev = parent->myDev;
-
- // Check if the entry exists. If it does then fail the call since we don't want a dup.
- if(yaffs_FindObjectByName(parent,name))
- {
- return NULL;
- }
-
- in = yaffs_CreateNewObject(dev,-1,type);
-
- if(in)
- {
- in->chunkId = -1;
- in->valid = 1;
- in->variantType = type;
-
- in->st_mode = mode;
- in->st_uid = uid;
- in->st_gid = gid;
- in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;
-
- in->sum = yaffs_CalcNameSum(name);
- in->dirty = 1;
-
- yaffs_AddObjectToDirectory(parent,in);
-
- in->myDev = parent->myDev;
-
-
- switch(type)
- {
- case YAFFS_OBJECT_TYPE_SYMLINK:
- in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- in->variant.hardLinkVariant.equivalentObject = equivalentObject;
- in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;
- list_add(&in->hardLinks,&equivalentObject->hardLinks);
- break;
- case YAFFS_OBJECT_TYPE_FILE: // do nothing
- case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- }
-
- yaffs_UpdateObjectHeader(in,name);
-
- }
-
- return in;
-}
-
-yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
-{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL);
-}
-
-yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
-{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL);
-}
-
-yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)
-{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias);
-}
-
-// NB yaffs_Link returns the object id of the equivalent object.
-yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject)
-{
- // Get the real object in case we were fed a hard link as an equivalent object
- equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
-
- if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL))
- {
- return equivalentObject;
- }
- else
- {
- return NULL;
- }
-
-}
-
-
-static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName)
-{
- //yaffs_Device *dev = obj->myDev;
-
- if(newDir == NULL)
- {
- newDir = obj->parent; // use the old directory
- }
-
- // Only proceed if the new name does not exist and
- // if we're putting it into a directory.
- if(!yaffs_FindObjectByName(newDir,newName) &&
- newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
- {
- obj->sum = yaffs_CalcNameSum(newName);
- obj->dirty = 1;
- yaffs_AddObjectToDirectory(newDir,obj);
-
- if(yaffs_UpdateObjectHeader(obj,newName) >= 0)
- {
- return YAFFS_OK;
- }
- }
-
- return YAFFS_FAIL;
-}
-
-int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)
-{
- yaffs_Object *obj;
-
- obj = yaffs_FindObjectByName(oldDir,oldName);
- if(obj && obj->renameAllowed)
- {
- return yaffs_ChangeObjectName(obj,newDir,newName);
- }
- return YAFFS_FAIL;
-}
-
-
-
-static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)
-{
- // Scan the buckets and check that the lists
- // have as many members as the count says there are
- int bucket;
- int countEm;
- struct list_head *j;
- int ok = YAFFS_OK;
-
- for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)
- {
- countEm = 0;
-
- list_for_each(j,&dev->objectBucket[bucket].list)
- {
- countEm++;
- }
-
- if(countEm != dev->objectBucket[bucket].count)
- {
- YALERT("Inode hash inconsistency");
- ok = YAFFS_FAIL;
- }
- }
-
- return ok;
-}
-
-void yaffs_ObjectTest(yaffs_Device *dev)
-{
- yaffs_Object *in[1000];
- int inNo[1000];
- yaffs_Object *inold[1000];
- int i;
- int j;
-
- memset(in,0,1000*sizeof(yaffs_Object *));
- memset(inold,0,1000*sizeof(yaffs_Object *));
-
- yaffs_CheckObjectHashSanity(dev);
-
- for(j = 0; j < 10; j++)
- {
- T(("%d\n",j));
-
- for(i = 0; i < 1000; i++)
- {
- in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);
- if(!in[i])
- {
- YINFO("No more inodes");
- }
- else
- {
- inNo[i] = in[i]->objectId;
- }
- }
-
- for(i = 0; i < 1000; i++)
- {
- if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])
- {
- T(("Differnce in look up test\n"));
- }
- else
- {
- // T(("Look up ok\n"));
- }
- }
-
- yaffs_CheckObjectHashSanity(dev);
-
- for(i = 0; i < 1000; i+=3)
- {
- yaffs_FreeObject(in[i]);
- in[i] = NULL;
- }
-
-
- yaffs_CheckObjectHashSanity(dev);
- }
-
-}
-
-
-
-/////////////////////////// Block Management and Page Allocation ///////////////////
-
-
-static void yaffs_InitialiseBlocks(yaffs_Device *dev)
-{
- dev->blockInfo = YMALLOC(dev->nBlocks * sizeof(yaffs_BlockInfo));
- memset(dev->blockInfo,0,dev->nBlocks * sizeof(yaffs_BlockInfo));
- dev->allocationBlock = -1; // force it to get a new one
-}
-
-static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
-{
- YFREE(dev->blockInfo);
-}
-
-// FindDiretiestBlock is used to select the dirtiest block (or close enough)
-// for garbage collection.
-
-static int yaffs_FindDirtiestBlock(yaffs_Device *dev)
-{
-
- int b = dev->currentDirtyChecker;
-
- int i;
- int dirtiest = -1;
- int pagesInUse = 100; // silly big number
-
- for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
- {
- b++;
- if (b > dev->endBlock)
- {
- b = dev->startBlock;
- }
-
- if(dev->blockInfo[b].blockState == YAFFS_BLOCK_STATE_FULL &&
- (dev->blockInfo)[b].pagesInUse < pagesInUse)
- {
- dirtiest = b;
- pagesInUse = (dev->blockInfo)[b].pagesInUse;
- }
- }
-
- dev->currentDirtyChecker = b;
-
- return dirtiest;
-}
-
-
-static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve)
-{
- int i;
-
- if(useReserve && dev->nErasedBlocks < 1)
- {
- // Hoosterman we've got a problem.
- // Can't get space to gc
- return -1;
- }
- else if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)
- {
- // We are not in GC, so we hold some in reserve so we can get
- // a gc done.
- }
-
- // Find an empty block.
-
- for(i = dev->startBlock; i <= dev->endBlock; i++)
- {
-
- if(dev->blockInfo[i].blockState == YAFFS_BLOCK_STATE_EMPTY)
- {
- dev->blockInfo[i].blockState = YAFFS_BLOCK_STATE_ALLOCATING;
- dev->nErasedBlocks--;
- if(dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)
- {
- dev->garbageCollectionRequired = 1;
- }
-
- return i;
- }
- }
-
- return -1;
-}
-
-
-static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
-{
- yaffs_BlockInfo *bi = &dev->blockInfo[blockNo];
-
- // Mark as dirty, erase it and mark as clean.
- bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
- yaffs_EraseBlockInNAND(dev,blockNo);
- bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
- dev->nErasedBlocks++;
- bi->pagesInUse = 0;
- bi->pageBits = 0;
-
- T(("Erased block %d\n",blockNo));
-}
-
-
-static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
-{
- int retVal;
-
- if(dev->allocationBlock < 0)
- {
- // Get next block to allocate off
- dev->allocationBlock = yaffs_FindBlockForAllocation(dev,useReserve);
- dev->allocationPage = 0;
- }
-
- // Next page please....
- if(dev->allocationBlock >= 0)
- {
- retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) +
- dev->allocationPage;
- dev->blockInfo[dev->allocationBlock].pagesInUse++;
- dev->blockInfo[dev->allocationBlock].pageBits |=
- (1 << (dev->allocationPage));
-
- dev->allocationPage++;
-
- dev->nFreeChunks--;
-
- // If the block is full set the state to full
- if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK)
- {
- dev->blockInfo[dev->allocationBlock].blockState = YAFFS_BLOCK_STATE_FULL;
- dev->allocationBlock = -1;
- }
-
-#ifdef YAFFS_PARANOID
- if(yaffs_CheckChunkErased(dev,retVal) == YAFFS_FAIL)
- {
- T(("..................Trying to allocate non-erased page %d\n",retVal));
- }
-#endif
- return retVal;
-
- }
- T(("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!\n"));
-
- return -1;
-}
-
-
-int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
-{
- int oldChunk;
- int newChunk;
- __u32 mask;
-
-
- yaffs_Spare spare;
- yaffs_Tags tags;
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
-
- yaffs_BlockInfo *bi = &dev->blockInfo[block];
-
- yaffs_Object *object;
-
- T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));
-
- for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK;
- mask && bi->pageBits;
- mask <<= 1, oldChunk++ )
- {
- if(bi->pageBits & mask)
- {
-
- // This page is in use and needs to be copied off
-
- T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
-
- yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare);
-
- yaffs_GetTagsFromSpare(&spare,&tags);
- tags.serialNumber++;
- yaffs_LoadTagsIntoSpare(&spare,&tags);
-
-#if 0
- newChunk = yaffs_AllocatePage(dev,1);
- if(newChunk < 0)
- {
- return YAFFS_FAIL;
- }
-
- yaffs_WriteChunkToNAND(dev,newChunk, buffer, &spare);
-
-#else
- newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
-#endif
- if(newChunk < 0)
- {
- return YAFFS_FAIL;
- }
-
- object = yaffs_FindObjectByNumber(dev,tags.objectId);
-
- // Ok, now fix up the Tnodes etc.
-
- if(tags.chunkId == 0)
- {
- // It's a header
- object->chunkId = newChunk;
- }
- else
- {
- // It's a data chunk
- yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
-
- }
-
- yaffs_DeleteChunk(dev,oldChunk);
-
- }
- }
-
- return YAFFS_OK;
-}
-
-int yaffs_CheckGarbageCollection(yaffs_Device *dev)
-{
- int block;
-
- if(dev->garbageCollectionRequired)
- {
- dev->garbageCollectionRequired = 0;
- block = yaffs_FindDirtiestBlock(dev);
- if(block >= 0)
- {
- return yaffs_GarbageCollectBlock(dev,block);
- }
- else
- {
- return YAFFS_FAIL;
- }
- }
-
- return YAFFS_OK;
-}
-
-
-//////////////////////////// TAGS ///////////////////////////////////////
-
-static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)
-{
- yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
-
- yaffs_CalcTagsECC(tagsPtr);
-
- sparePtr->tagByte0 = tu->asBytes[0];
- sparePtr->tagByte1 = tu->asBytes[1];
- sparePtr->tagByte2 = tu->asBytes[2];
- sparePtr->tagByte3 = tu->asBytes[3];
- sparePtr->tagByte4 = tu->asBytes[4];
- sparePtr->tagByte5 = tu->asBytes[5];
- sparePtr->tagByte6 = tu->asBytes[6];
- sparePtr->tagByte7 = tu->asBytes[7];
-}
-
-static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
-{
- yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
-
- tu->asBytes[0]= sparePtr->tagByte0;
- tu->asBytes[1]= sparePtr->tagByte1;
- tu->asBytes[2]= sparePtr->tagByte2;
- tu->asBytes[3]= sparePtr->tagByte3;
- tu->asBytes[4]= sparePtr->tagByte4;
- tu->asBytes[5]= sparePtr->tagByte5;
- tu->asBytes[6]= sparePtr->tagByte6;
- tu->asBytes[7]= sparePtr->tagByte7;
-
- yaffs_CheckECCOnTags(tagsPtr);
-}
-
-static void yaffs_SpareInitialise(yaffs_Spare *spare)
-{
- memset(spare,0xFF,sizeof(yaffs_Spare));
-}
-
-static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags)
-{
- if(tags)
- {
- yaffs_Spare spare;
- if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare) == YAFFS_OK)
- {
- yaffs_GetTagsFromSpare(&spare,tags);
- return YAFFS_OK;
- }
- else
- {
- return YAFFS_FAIL;
- }
- }
-
- return YAFFS_OK;
-}
-
-#if 0
-static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags)
-{
- // NB There must be tags, data is optional
- // If there is data, then an ECC is calculated on it.
-
- yaffs_Spare spare;
-
- if(!tags)
- {
- return YAFFS_FAIL;
- }
-
- yaffs_SpareInitialise(&spare);
-
-
- if(buffer)
- {
- yaffs_CalcECC(buffer,&spare);
- }
-
- yaffs_LoadTagsIntoSpare(&spare,tags);
-
- return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);
-
-}
-#endif
-
-
-static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)
-{
- // NB There must be tags, data is optional
- // If there is data, then an ECC is calculated on it.
-
- yaffs_Spare spare;
-
- if(!tags)
- {
- return YAFFS_FAIL;
- }
-
- yaffs_SpareInitialise(&spare);
-
-
- if(buffer)
- {
- yaffs_CalcECC(buffer,&spare);
- }
-
- yaffs_LoadTagsIntoSpare(&spare,tags);
-
- return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);
-
-}
-
-
-
-
-int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
-{
- //Get the Tnode, then get the level 0 offset chunk offset
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_Tags localTags;
- int i;
- int found = 0;
- yaffs_Device *dev = in->myDev;
-
-
- if(!tags)
- {
- // Passed a NULL, so use our own tags space
- tags = &localTags;
- }
-
- tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
-
- if(tn)
- {
- theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
-
- // Now we need to do the shifting etc and search for it
- for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
- {
- yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags);
- if(tags->chunkId == chunkInInode &&
- tags->objectId == in->objectId)
- {
- // found it;
- found = 1;
- }
- else
- {
- theChunk++;
- }
- }
- }
- return found ? theChunk : -1;
-}
-
-int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
-{
- //Get the Tnode, then get the level 0 offset chunk offset
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_Tags localTags;
- int i;
- int found = 0;
- yaffs_Device *dev = in->myDev;
-
- if(!tags)
- {
- // Passed a NULL, so use our own tags space
- tags = &localTags;
- }
-
- tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
-
- if(tn)
- {
-
- theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
-
- // Now we need to do the shifting etc and search for it
- for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
- {
- yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags);
- if(tags->chunkId == chunkInInode &&
- tags->objectId == in->objectId)
- {
- // found it;
- found = 1;
- }
- else
- {
- theChunk++;
- }
- }
-
- // Delete the entry in the filestructure
- if(found)
- {
- tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;
- }
- }
- else
- {
- T(("No level 0 found for %d\n", chunkInInode));
- }
-
- if(!found)
- {
- T(("Could not find %d to delete\n",chunkInInode));
- }
- return found ? theChunk : -1;
-}
-
-
-#if YAFFS_PARANOID
-
-static int yaffs_CheckFileSanity(yaffs_Object *in)
-{
- int chunk;
- int nChunks;
- int fSize;
- int failed = 0;
- int objId;
- yaffs_Tnode *tn;
- yaffs_Tags localTags;
- yaffs_Tags *tags = &localTags;
- int theChunk;
-
-
- if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
- {
- T(("Object not a file\n"));
- return YAFFS_FAIL;
- }
-
- objId = in->objectId;
- fSize = in->variant.fileVariant.fileSize;
- nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK;
-
- for(chunk = 1; chunk <= nChunks; chunk++)
- {
- tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);
-
- if(tn)
- {
-
- theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;
-
-
- yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags);
- if(tags->chunkId == chunk &&
- tags->objectId == in->objectId)
- {
- // found it;
-
- }
- else
- {
- //T(("File problem file [%d,%d] NAND %d tags[%d,%d]\n",
- // objId,chunk,theChunk,tags->chunkId,tags->objectId);
-
- failed = 1;
-
- }
-
- }
- else
- {
- T(("No level 0 found for %d\n", chunk));
- }
- }
-
- return failed ? YAFFS_FAIL : YAFFS_OK;
-}
-
-#endif
-
-static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
-{
- yaffs_Tnode *tn;
- yaffs_Device *dev = in->myDev;
- int existingChunk;
- yaffs_Tags existingTags;
- yaffs_Tags newTags;
- unsigned existingSerial, newSerial;
-
-
- tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
-
- if(inScan)
- {
- // 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.
-
- existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];
-
- if(existingChunk != 0)
- {
- // 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_ReadChunkTagsFromNAND(dev,chunkInInode, &newTags);
- yaffs_ReadChunkTagsFromNAND(dev,existingChunk, &existingTags);
- newSerial = newTags.serialNumber;
- existingSerial = existingTags.serialNumber;
- if(((existingSerial+1) & 3) == newSerial)
- {
- // Use new
- // Delete the old one and drop through to update the tnode
- yaffs_DeleteChunk(dev,existingChunk);
- }
- else
- {
- // Use existing.
- // Delete the new one and return early so that the tnode isn't changed
- yaffs_DeleteChunk(dev,chunkInInode);
- return YAFFS_OK;
- }
- }
- }
-
- tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = chunkInNAND;
-
- return YAFFS_OK;
-}
-
-
-
-int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
-{
- int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
-
- if(chunkInNAND >= 0)
- {
- return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL);
- }
- else
- {
- return 0;
- }
-
-}
-
-
-static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
-{
- int block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
- int page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
- yaffs_Spare spare;
-
- yaffs_SpareInitialise(&spare);
-
- spare.pageStatus = 0; // To mark it as deleted.
-
-
- yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
-
-
- // Pull out of the management area.
- // If the whole block became dirty, this will kick off an erasure.
- if( dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
- dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)
- {
- dev->nFreeChunks++;
-
- dev->blockInfo[block].pageBits &= ~(1 << page);
- dev->blockInfo[block].pagesInUse--;
-
- if( dev->blockInfo[block].pagesInUse == 0 &&
- dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)
- {
- yaffs_BlockBecameDirty(dev,block);
- }
-
- }
- else
- {
- T(("Bad news deleting chunk %d\n",chunkId));
- }
-
-}
-
-
-
-
-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.
- // Invalidate old tags.
-
- int prevChunkId;
- yaffs_Tags prevTags;
-
- int newChunkId;
- yaffs_Tags newTags;
-
- yaffs_Device *dev = in->myDev;
-
- yaffs_CheckGarbageCollection(dev);
-
- // Get the previous chunk at this location in the file if it exists
- prevChunkId = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);
-
- // Set up new tags
- newTags.chunkId = chunkInInode;
- newTags.objectId = in->objectId;
- newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
- newTags.byteCount = nBytes;
- newTags.unusedStuff = 0xFFFFFFFF;
-
- yaffs_CalcTagsECC(&newTags);
-
-
- #if 0
- // Create new chunk in NAND
- newChunkId = yaffs_AllocatePage(dev,useReserve);
-
-
- if(newChunkId >= 0)
- {
-
-
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,buffer,&newTags);
-
- yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId);
-
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
-
- }
-
- yaffs_CheckFileSanity(in);
-
- return newChunkId;
- }
-
-
- return -1;
-#else
-
- newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
- if(newChunkId >= 0)
- {
- yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
-
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
-
- }
-
- yaffs_CheckFileSanity(in);
- }
- return newChunkId;
-
-#endif
-
-
-
-}
-
-
-// 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 char *name)
-{
-
- yaffs_Device *dev = in->myDev;
-
- int prevChunkId;
-
- int newChunkId;
- yaffs_Tags newTags;
- __u8 bufferNew[YAFFS_BYTES_PER_CHUNK];
- __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];
-
- yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;
- yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
-
- if(!in->fake)
- {
-
- yaffs_CheckGarbageCollection(dev);
-
- memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK);
-
- prevChunkId = in->chunkId;
-
- if(prevChunkId >= 0)
- {
- yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL);
- }
-
- // Header data
- oh->type = in->variantType;
-
- oh->st_mode = in->st_mode;
- 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->parentObjectId = in->parent->objectId;
- oh->sum = in->sum;
- if(name && *name)
- {
- memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);
- strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
- }
- else
- {
- memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);
- }
-
- switch(in->variantType)
- {
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- // Should not happen
- break;
- case YAFFS_OBJECT_TYPE_FILE:
- oh->fileSize = in->variant.fileVariant.fileSize;
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- // Do nothing
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
- oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
- break;
- }
-
- // Tags
- in->serial++;
- newTags.chunkId = 0;
- newTags.objectId = in->objectId;
- newTags.serialNumber = in->serial;
- newTags.byteCount = 0xFFFFFFFF;
- newTags.unusedStuff = 0xFFFFFFFF;
-
- yaffs_CalcTagsECC(&newTags);
-
-
-
-#if 0
- // Create new chunk in NAND
- newChunkId = yaffs_AllocatePage(dev,1);
-
- if(newChunkId >= 0)
- {
-
- yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags);
-
- in->chunkId = newChunkId;
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
- }
-
- in->dirty = 0;
- return newChunkId;
- }
-
- return -1;
-#else
- // Create new chunk in NAND
- newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags,1);
-
- if(newChunkId >= 0)
- {
-
- in->chunkId = newChunkId;
-
- if(prevChunkId >= 0)
- {
- yaffs_DeleteChunk(dev,prevChunkId);
- }
-
- in->dirty = 0;
- }
-
- return newChunkId;
-
-#endif
- }
- return 0;
-}
-
-
-
-///////////////////////// File read/write ///////////////////////////////
-// Read and write have very similar structures.
-// In general the read/write has three parts to it
-// * An incomplete chunk to start with (if the read/write is not chunk-aligned)
-// * Some complete chunks
-// * An incomplete chunk to end off with
-//
-// Curve-balls: the first chunk might also be the last chunk.
-
-int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)
-{
-
-// yaffs_Device *dev = in->myDev;
-
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
-
- int chunk;
- int start;
- int nToCopy;
- int n = nBytes;
- int nDone = 0;
-
- while(n > 0)
- {
- chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1
- start = offset % YAFFS_BYTES_PER_CHUNK;
-
- // OK now check for the curveball where the start and end are in
- // the same chunk.
- if( (start + n) < YAFFS_BYTES_PER_CHUNK)
- {
- nToCopy = n;
- }
- else
- {
- nToCopy = YAFFS_BYTES_PER_CHUNK - start;
- }
-
- if(nToCopy != YAFFS_BYTES_PER_CHUNK)
- {
- // An incomplete start or end chunk (or maybe both start and end chunk)
- // Read into the local buffer then copy...
-
- yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
- memcpy(buffer,&localBuffer[start],nToCopy);
- }
- else
- {
- // A full chunk. Read directly into the supplied buffer.
- yaffs_ReadChunkDataFromObject(in,chunk,buffer);
- }
-
- n -= nToCopy;
- offset += nToCopy;
- buffer += nToCopy;
- nDone += nToCopy;
-
- }
-
- return nDone;
-}
-
-
-int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)
-{
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
-
- int chunk;
- int start;
- int nToCopy;
- int n = nBytes;
- int nDone = 0;
- int nToWriteBack;
- int endOfWrite = offset+nBytes;
- int chunkWritten = 0;
-
- while(n > 0 && chunkWritten >= 0)
- {
- chunk = offset / YAFFS_BYTES_PER_CHUNK + 1;
- start = offset % YAFFS_BYTES_PER_CHUNK;
-
-
- // OK now check for the curveball where the start and end are in
- // the same chunk.
- if( (start + n) < YAFFS_BYTES_PER_CHUNK)
- {
- nToCopy = n;
- nToWriteBack = (start + n);
- }
- else
- {
- nToCopy = YAFFS_BYTES_PER_CHUNK - start;
- nToWriteBack = YAFFS_BYTES_PER_CHUNK;
- }
-
- if(nToCopy != YAFFS_BYTES_PER_CHUNK)
- {
- // An incomplete start or end chunk (or maybe both start and end chunk)
- // Read into the local buffer then copy, then copy over and write back.
-
- yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
-
- memcpy(&localBuffer[start],buffer,nToCopy);
-
- chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);
-
- T(("Write with readback to chunk %d %d\n",chunk,chunkWritten));
-
- }
- else
- {
- // A full chunk. Write directly from the supplied buffer.
- chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);
- T(("Write to chunk %d %d\n",chunk,chunkWritten));
- }
-
- if(chunkWritten >= 0)
- {
- n -= nToCopy;
- offset += nToCopy;
- buffer += nToCopy;
- nDone += nToCopy;
- }
-
- }
-
- // Update file object
-
- if(endOfWrite > in->variant.fileVariant.fileSize)
- {
- in->variant.fileVariant.fileSize = endOfWrite;
- }
-
- in->dirty = 1;
- in->st_mtime = CURRENT_TIME;
-
- return nDone;
-}
-
-
-int yaffs_ResizeFile(yaffs_Object *in, int newSize)
-{
- int i;
- int chunkId;
- int oldFileSize = in->variant.fileVariant.fileSize;
- int sizeOfLastChunk = newSize % YAFFS_BYTES_PER_CHUNK;
-
- yaffs_Device *dev = in->myDev;
-
- __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];
-
- if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
- {
- return yaffs_GetFileSize(in);
- }
-
- if(newSize < oldFileSize)
- {
-
- int lastDel = 1 + oldFileSize/YAFFS_BYTES_PER_CHUNK;
-
- int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/
- YAFFS_BYTES_PER_CHUNK;
-
- for(i = startDel; i <= lastDel; i++)
- {
- // NB this could be optimised somewhat,
- // eg. could retrieve the tags and write them without
- // using yaffs_DeleteChunk
- chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
- if(chunkId < 0 || chunkId >= (dev->endBlock * 32))
- {
- T(("Found daft chunkId %d for %d\n",chunkId,i));
- }
- else
- {
- yaffs_DeleteChunk(dev,chunkId);
- }
- }
-
-
- if(sizeOfLastChunk != 0)
- {
- int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;
-
- // Got to read and rewrite the last chunk with its new size.
- yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);
-
- yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfLastChunk,1);
-
- }
-
- in->variant.fileVariant.fileSize = newSize;
-
- yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
-
- return newSize;
-
- }
- else
- {
- return oldFileSize;
- }
-}
-
-
-loff_t yaffs_GetFileSize(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
-
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- return obj->variant.fileVariant.fileSize;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- return strlen(obj->variant.symLinkVariant.alias);
- default:
- return 0;
- }
-}
-
-
-
-// yaffs_FlushFile() updates the file's
-// objectId in NAND
-
-int yaffs_FlushFile(yaffs_Object *in)
-{
- int retVal;
- if(in->dirty)
- {
- T(("flushing object header\n"));
- retVal = yaffs_UpdateObjectHeader(in,NULL);
- }
- else
- {
- retVal = YAFFS_OK;
- }
-
- return retVal;
-
-}
-
-
-static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
-{
- yaffs_RemoveObjectFromDirectory(in);
- yaffs_DeleteChunk(in->myDev,in->chunkId);
- yaffs_FreeObject(in);
- return YAFFS_OK;
-
-}
-
-// yaffs_DeleteFile deletes the whole file data
-// and the inode associated with the file.
-// It does not delete the links associated with the file.
-static int yaffs_DeleteFile(yaffs_Object *in)
-{
- // Delete the file data & tnodes
- yaffs_ResizeFile(in,0);
- yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
-
- return yaffs_DoGenericObjectDeletion(in);
-}
-
-static int yaffs_DeleteDirectory(yaffs_Object *in)
-{
- //First check that the directory is empty.
- if(list_empty(&in->variant.directoryVariant.children))
- {
- return yaffs_DoGenericObjectDeletion(in);
- }
-
- return YAFFS_FAIL;
-
-}
-
-static int yaffs_DeleteSymLink(yaffs_Object *in)
-{
- YFREE(in->variant.symLinkVariant.alias);
-
- return yaffs_DoGenericObjectDeletion(in);
-}
-
-static int yaffs_DeleteHardLink(yaffs_Object *in)
-{
- // remove this hardlink from the list assocaited with the equivalent
- // object
- list_del(&in->hardLinks);
- return yaffs_DoGenericObjectDeletion(in);
-}
-
-
-static int yaffs_UnlinkWorker(yaffs_Object *obj)
-{
-
-
- if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
- {
- return yaffs_DeleteHardLink(obj);
- }
- else if(!list_empty(&obj->hardLinks))
- {
-#if 0
- // Curve ball: We're unlinking an object that has a hardlink.
- // Therefore we can't really delete the object.
- // Instead, we do the following:
- // - Select a hardlink.
- // - Re-type a hardlink as the equivalent object and populate the fields, including the
- // objectId. Updating the object id is important so that all the hardlinks do not need
- // to be rewritten.
- // - Update the equivalet object pointers.
- // - Delete all object.
-
- yaffs_Object *hl;
- struct list_head *i;
-
-
- yaffs_RemoveObjectFromDirectory(obj);
-
-
-
- hl = list_entry(obj->hardLinks.next, yaffs_Object,hardLinks);
-
- hl->dirty = 1;
- hl->st_mode = obj->st_mode;
- hl->st_uid = obj->st_uid;
- hl->st_gid = obj->st_gid;
- hl->st_atime = obj->st_atime;
- hl->st_mtime = obj->st_mtime;
- hl->st_ctime = obj->st_ctime;
-
- hl->variantType = obj->variantType;
-
- switch(hl->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- case YAFFS_OBJECT_TYPE_SYMLINK:
- // These types are OK to just copy across.
- hl->variant = obj->variant;
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- // Fix the list up
- list_add(&hl->variant.directoryVariant.children,
- &obj->variant.directoryVariant.children);
- list_del(&obj->variant.directoryVariant.children);
-
- // Now change all the directory children to point to the new parent.
- list_for_each(i,&hl->variant.directoryVariant.children)
- {
- list_entry(i,yaffs_Object,siblings)->parent = hl;
- }
- break;
-
- case YAFFS_OBJECT_TYPE_HARDLINK:
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- // Should not be either of these types.
- }
-
- // Now fix up the hardlink chain
- list_del(&obj->hardLinks);
-
- list_for_each(i,&hl->hardLinks)
- {
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObject = hl;
- list_entry(i,yaffs_Object,hardLinks)->variant.hardLinkVariant.equivalentObjectId = hl->objectId;
- }
-
- // Now fix up the hash links.
- yaffs_UnhashObject(hl);
- hl->objectId = obj->objectId;
- yaffs_HashObject(hl);
-
- // Update the hardlink which has become an object
- yaffs_UpdateObjectHeader(hl,NULL);
-
- // Finally throw away the deleted object
- yaffs_DeleteChunk(obj->myDev,obj->chunkId);
- yaffs_FreeObject(obj);
-
- return YAFFS_OK;
-#else
- // Curve ball: We're unlinking an object that has a hardlink.
- //
- // This problem arises because we are not strictly following
- // The Linux link/inode model.
- //
- // We can't really delete the object.
- // Instead, we do the following:
- // - Select a hardlink.
- // - Unhook it from the hard links
- // - Unhook it from its parent directory (so that the rename can work)
- // - Rename the object to the hardlink's name.
- // - Delete the hardlink
-
-
- yaffs_Object *hl;
- int retVal;
- char name[YAFFS_MAX_NAME_LENGTH+1];
-
- hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);
-
- list_del_init(&hl->hardLinks);
- list_del_init(&hl->siblings);
-
- yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
-
- retVal = yaffs_ChangeObjectName(obj, hl->parent, name);
-
- if(retVal == YAFFS_OK)
- {
- retVal = yaffs_DoGenericObjectDeletion(hl);
- }
- return retVal;
-
-#endif
-
-
- }
- else
- {
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- return yaffs_DeleteFile(obj);
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- return yaffs_DeleteDirectory(obj);
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- return yaffs_DeleteSymLink(obj);
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- default:
- return YAFFS_FAIL;
- }
- }
-}
-
-int yaffs_Unlink(yaffs_Object *dir, const char *name)
-{
- yaffs_Object *obj;
-
- obj = yaffs_FindObjectByName(dir,name);
-
- if(obj && obj->unlinkAllowed)
- {
- return yaffs_UnlinkWorker(obj);
- }
-
- return YAFFS_FAIL;
-
-}
-
-//////////////// Initialisation Scanning /////////////////
-
-
-static int yaffs_Scan(yaffs_Device *dev)
-{
- yaffs_Spare spare;
- yaffs_Tags tags;
- int blk;
- int chunk;
- int c;
- int deleted;
- yaffs_BlockState state;
- yaffs_Object *hardList = NULL;
- yaffs_Object *hl;
-
-// int inuse;
-// __u32 pageBits;
-
- yaffs_ObjectHeader *oh;
- yaffs_Object *in;
- yaffs_Object *parent;
-
- __u8 chunkData[YAFFS_BYTES_PER_CHUNK];
-
-
- // Scan all the blocks...
-
- for(blk = dev->startBlock; blk <= dev->endBlock; blk++)
- {
- deleted = 0;
- dev->blockInfo[blk].pageBits = 0;
- dev->blockInfo[blk].pagesInUse = 0;
- state = YAFFS_BLOCK_STATE_SCANNING;
-
- // Read each chunk in the block.
-
- for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK &&
- state == YAFFS_BLOCK_STATE_SCANNING; c++)
- {
- // Read the spare area and decide what to do
- chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c;
-
- yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare);
-
-
- // Is this a valid block?
- if(yaffs_countBits[spare.blockStatus] >= 7)
- {
- // This block looks ok, now what's in this chunk?
- yaffs_GetTagsFromSpare(&spare,&tags);
-
- if(yaffs_countBits[spare.pageStatus] < 6)
- {
- // A deleted chunk
- deleted++;
- dev->nFreeChunks ++;
- T((" %d %d deleted\n",blk,c));
- }
- else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)
- {
- // 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)
- {
- // the block is unused
- state = YAFFS_BLOCK_STATE_EMPTY;
- dev->nErasedBlocks++;
- }
- else
- {
- // this is the block being allocated from
- T((" allocating %d %d\n",blk,c));
- state = YAFFS_BLOCK_STATE_ALLOCATING;
- dev->allocationBlock = blk;
- dev->allocationPage = c;
- }
-
- dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);
- }
- else if(tags.chunkId > 0)
- {
- // A data chunk.
- dev->blockInfo[blk].pageBits |= (1 << c);
- dev->blockInfo[blk].pagesInUse++;
-
- in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
- // PutChunkIntoFIle checks for a clash (two data chunks with
- // the same chunkId).
- yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
- 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
- dev->blockInfo[blk].pageBits |= (1 << c);
- dev->blockInfo[blk].pagesInUse++;
-
- yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL);
-
- oh = (yaffs_ObjectHeader *)chunkData;
-
- in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
-
- if(in->valid)
- {
- // We have already filled this one. We have a duplicate and need to resolve it.
-
- unsigned existingSerial = in->serial;
- unsigned newSerial = tags.serialNumber;
-
- if(((existingSerial+1) & 3) == newSerial)
- {
- // Use new one - destroy the exisiting one
- yaffs_DeleteChunk(dev,in->chunkId);
- in->valid = 0;
- }
- else
- {
- // Use existing - destroy this one.
- yaffs_DeleteChunk(dev,chunk);
- }
- }
-
- if(!in->valid)
- {
- // we need to load this info
-
- in->valid = 1;
- in->variantType = oh->type;
-
- in->st_mode = oh->st_mode;
- 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->chunkId = chunk;
-
- in->sum = oh->sum;
- 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
- // Todo ... handle
- }
-
- yaffs_AddObjectToDirectory(parent,in);
-
- // 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:
- in->variant.fileVariant.fileSize = oh->fileSize;
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
- (yaffs_Object *)(in->hardLinks.next) = hardList;
- hardList = in;
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing
- in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
- break;
- }
- T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
- }
- }
- }
- else
- {
- // it's a bad block
- state = YAFFS_BLOCK_STATE_DEAD;
- }
- }
-
- if(state == YAFFS_BLOCK_STATE_SCANNING)
- {
- // If we got this far while scanning, then the block is fully allocated.
- state = YAFFS_BLOCK_STATE_FULL;
- }
-
- dev->blockInfo[blk].blockState = state;
-
- // Now let's see if it was dirty
- if( dev->blockInfo[blk].pagesInUse == 0 &&
- dev->blockInfo[blk].blockState == YAFFS_BLOCK_STATE_FULL)
- {
- yaffs_BlockBecameDirty(dev,blk);
- }
-
- }
-
- // 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);
-
- }
-
- }
-
-
-
- return YAFFS_OK;
-}
-
-
-////////////////////////// Directory Functions /////////////////////////
-
-
-static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
-{
-
- if(obj->siblings.prev == NULL)
- {
- // Not initialised
- INIT_LIST_HEAD(&obj->siblings);
-
- }
- else if(!list_empty(&obj->siblings))
- {
- // If it is holed up somewhere else, un hook it
- list_del_init(&obj->siblings);
- }
- // Now add it
- list_add(&obj->siblings,&directory->variant.directoryVariant.children);
- obj->parent = directory;
-}
-
-static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
-{
- list_del_init(&obj->siblings);
- obj->parent = NULL;
-}
-
-yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)
-{
- int sum;
-
- struct list_head *i;
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
- yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
-
- yaffs_Object *l;
-
- sum = yaffs_CalcNameSum(name);
-
- list_for_each(i,&directory->variant.directoryVariant.children)
- {
- l = list_entry(i, yaffs_Object,siblings);
-
- // Special case for lost-n-found
- if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
- {
- if(strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
- {
- return l;
- }
- }
- else if(l->sum == sum)
- {
- // Do a real check
- yaffs_ReadChunkFromNAND(l->myDev,l->chunkId,buffer,NULL);
- if(strcmp(name,oh->name) == 0)
- {
- return l;
- }
-
-
- }
- }
-
- return NULL;
-}
-
-
-int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *))
-{
- struct list_head *i;
- yaffs_Object *l;
-
-
- list_for_each(i,&theDir->variant.directoryVariant.children)
- {
- l = list_entry(i, yaffs_Object,siblings);
- if(!fn(l))
- {
- return YAFFS_FAIL;
- }
- }
-
- return YAFFS_OK;
-
-}
-
-
-// GetEquivalentObject dereferences any hard links to get to the
-// actual object.
-
-yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)
-{
- if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
- {
- // We want the object id of the equivalent object, not this one
- obj = obj->variant.hardLinkVariant.equivalentObject;
- }
- return obj;
-
-}
-
-int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize)
-{
- memset(name,0,buffSize);
-
- if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)
- {
- strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);
- }
- else
- {
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
- yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
-
- memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
-
- if(obj->chunkId >= 0)
- {
- yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
- }
- strncpy(name,oh->name,buffSize - 1);
- }
-
- return strlen(name);
-}
-
-int yaffs_GetObjectFileLength(yaffs_Object *obj)
-{
-
- // Dereference any hard linking
- obj = yaffs_GetEquivalentObject(obj);
-
- if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
- {
- return obj->variant.fileVariant.fileSize;
- }
- if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
- {
- return strlen(obj->variant.symLinkVariant.alias);
- }
- else
- {
- // Only a directory should drop through to here
- return YAFFS_BYTES_PER_CHUNK;
- }
-}
-
-int yaffs_GetObjectLinkCount(yaffs_Object *obj)
-{
- int count = 1; // the object itself
- struct list_head *i;
-
- list_for_each(i,&obj->hardLinks)
- {
- count++;
- }
- return count;
-
-}
-
-
-int yaffs_GetObjectInode(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
-
- return obj->objectId;
-}
-
-unsigned yaffs_GetObjectType(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
-
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE: return DT_REG; break;
- case YAFFS_OBJECT_TYPE_DIRECTORY: return DT_DIR; break;
- case YAFFS_OBJECT_TYPE_SYMLINK: return DT_LNK; break;
- case YAFFS_OBJECT_TYPE_HARDLINK: return DT_REG; break;
- default: return DT_REG; break;
- }
-}
-
-char *yaffs_GetSymlinkAlias(yaffs_Object *obj)
-{
- obj = yaffs_GetEquivalentObject(obj);
- if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
- {
- return yaffs_CloneString(obj->variant.symLinkVariant.alias);
- }
- else
- {
- return yaffs_CloneString("");
- }
-}
-
-
-int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
-{
- 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_ATIME) obj->st_atime = attr->ia_atime;
- if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime;
- if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime;
-
- if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
-
- yaffs_UpdateObjectHeader(obj,NULL);
-
- return YAFFS_OK;
-
-}
-int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
-{
- 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_atime = obj->st_atime; valid |= ATTR_ATIME;
- attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME;
- attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME;
-
- attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
-
- attr->ia_valid = valid;
-
- return YAFFS_OK;
-
-}
-
-
-
-int yaffs_DumpObject(yaffs_Object *obj)
-{
- __u8 buffer[YAFFS_BYTES_PER_CHUNK];
- char name[256];
-// yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
-
- memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
-
- if(obj->chunkId >= 0)
- {
- yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
- }
-
- yaffs_GetObjectName(obj,name,256);
-
- YPRINTF(("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n",
- obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial,
- obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
-
-#if 0
- YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n",
- obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial,
- obj->sum, obj->chunkId));
- switch(obj->variantType)
- {
- case YAFFS_OBJECT_TYPE_FILE:
- YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize));
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- YPRINTF((" DIRECTORY\n"));
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK: //todo
- case YAFFS_OBJECT_TYPE_SYMLINK:
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- default:
- }
-#endif
-
- return YAFFS_OK;
-}
-
-
-///////////////////////// Initialisation code ///////////////////////////
-
-
-
-int yaffs_GutsInitialise(yaffs_Device *dev)
-{
- unsigned nChunks,x;
- int bits;
-
-
- dev = dev;
-
- if(!yaffs_CheckStructures())
- {
- return YAFFS_FAIL;
- }
-
-
- // OK now calculate a few things for the device
- // Calculate chunkGroupBits.
- // If there are 64k or less chunks then this is 1
- // Else it is log2(nChunks) - 16
- //
- x = nChunks = YAFFS_CHUNKS_PER_BLOCK * dev->nBlocks;
-
- for(bits = 0, x = nChunks; (x & 1) == 0; bits++)
- {
- x >>= 1;
- }
-
- if( x != 1)
- {
- // Not a power of 2
- YPRINTF(("nBlocks should be a power of 2 but is %u\n",
- dev->nBlocks));
- return YAFFS_FAIL;
- }
-
- if(bits <= 16)
- {
- dev->chunkGroupBits = 0;
- dev->chunkGroupSize = 1;
- }
- else
- {
- dev->chunkGroupBits = bits - 16;
- dev->chunkGroupSize = nChunks/0x10000;
- }
-
- // More device initialisation
- dev->garbageCollectionRequired = 0;
- dev->currentDirtyChecker = 0;
-
- yaffs_InitialiseBlocks(dev);
-
- yaffs_InitialiseTnodes(dev);
-
- yaffs_InitialiseObjects(dev);
-
-
- // Initialise the root and lost and found directories
- dev->lostNFoundDir = dev->rootDir = NULL;
- dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);
- dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_ROOT_MODE | S_IFDIR);
- yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);
-
- // Now scan the flash.
- yaffs_Scan(dev);
-
-
- return YAFFS_OK;
-
-}
-
-void yaffs_Deinitialise(yaffs_Device *dev)
-{
- yaffs_DeinitialiseBlocks(dev);
- yaffs_DeinitialiseTnodes(dev);
- yaffs_DeinitialiseObjects(dev);
-
-}
-
-int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
-{
- int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);
-
- return (nFree < 0) ? 0 : nFree;
-
-}
-
-
-/////////////////// YAFFS test code //////////////////////////////////
-
-#define yaffs_CheckStruct(structure,syze, name) \
- if(sizeof(structure) != syze) \
- { YPRINTF(("%s should be %d but is %d\n",name,syze,sizeof(structure))); \
- return YAFFS_FAIL; \
- }
-
-
-static int yaffs_CheckStructures(void)
-{
- yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")
- yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")
- yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")
- yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
- yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")
-
-
- return YAFFS_OK;
-}
-
-void yaffs_GutsTest(yaffs_Device *dev)
-{
-
- if(yaffs_CheckStructures() != YAFFS_OK)
- {
- YPRINTF(("One or more structures malformed-- aborting\n"));
- }
- else
- {
- YPRINTF(("Structures OK\n"));
- }
-
- yaffs_TnodeTest(dev);
- yaffs_ObjectTest(dev);
-}
-
-
+/*\r
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.\r
+ * yaffs_guts.c The main guts of YAFFS\r
+ *\r
+ * Copyright (C) 2002 Aleph One Ltd.\r
+ * for Toby Churchill Ltd and Brightstar Engineering\r
+ *\r
+ * Created by Charles Manning <charles@aleph1.co.uk>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2 as\r
+ * published by the Free Software Foundation.\r
+ *\r
+ */\r
+ //yaffs_guts.c\r
+\r
+#include "yportenv.h"\r
+\r
+#include "yaffsinterface.h"\r
+#include "yaffs_guts.h"\r
+\r
+\r
+\r
+// External functions for ECC on data\r
+void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);\r
+int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);\r
+\r
+\r
+// countBits is a quick way of counting the number of bits in a byte.\r
+// ie. countBits[n] holds the number of 1 bits in a byte with the value n.\r
+\r
+static const char yaffs_countBits[256] =\r
+{\r
+0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,\r
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,\r
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,\r
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,\r
+4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8\r
+};\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 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 void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);\r
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);\r
+static int yaffs_CheckStructures(void);\r
+\r
+loff_t yaffs_GetFileSize(yaffs_Object *obj);\r
+\r
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted);\r
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted);\r
+\r
+\r
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);\r
+\r
+#ifdef YAFFS_PARANOID\r
+static int yaffs_CheckFileSanity(yaffs_Object *in);\r
+#else\r
+#define yaffs_CheckFileSanity(in)\r
+#endif\r
+\r
+static int __inline__ yaffs_HashFunction(int n)\r
+{\r
+ return (n % YAFFS_NOBJECT_BUCKETS);\r
+}\r
+\r
+\r
+yaffs_Object *yaffs_Root(yaffs_Device *dev)\r
+{\r
+ return dev->rootDir;\r
+}\r
+\r
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)\r
+{\r
+ return dev->lostNFoundDir;\r
+}\r
+\r
+\r
+static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)\r
+{\r
+ dev->nPageWrites++;\r
+ return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);\r
+}\r
+\r
+int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)\r
+{\r
+ int retVal;\r
+ __u8 calcEcc[3];\r
+ yaffs_Spare localSpare;\r
+ int eccResult1,eccResult2;\r
+ \r
+ dev->nPageReads++;\r
+ \r
+ if(!spare && data)\r
+ {\r
+ // If we don't have a real spare, then we use a local one.\r
+ // Need this for the calculation of the ecc\r
+ spare = &localSpare;\r
+ }\r
+ \r
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);\r
+ if(data)\r
+ {\r
+ // Do ECC correction\r
+ //Todo handle any errors\r
+ nand_calculate_ecc(data,calcEcc);\r
+ eccResult1 = nand_correct_data (data,spare->ecc1, calcEcc);\r
+ nand_calculate_ecc(&data[256],calcEcc);\r
+ eccResult2 = nand_correct_data (&data[256],spare->ecc2, calcEcc);\r
+ \r
+ if(eccResult1>0)\r
+ {\r
+ T((TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));\r
+ }\r
+ else if(eccResult1<0)\r
+ {\r
+ T((TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));\r
+ }\r
+ \r
+ if(eccResult2>0)\r
+ {\r
+ T((TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));\r
+ }\r
+ else if(eccResult2<0)\r
+ {\r
+ T((TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));\r
+ }\r
+ }\r
+ return retVal;\r
+}\r
+\r
+#ifdef YAFFS_PARANOID\r
+\r
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)\r
+{\r
+ static int init = 0;\r
+ static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];\r
+ static __u8 data[YAFFS_BYTES_PER_CHUNK];\r
+ static __u8 spare[16];\r
+ \r
+ int retVal;\r
+ \r
+ retVal = YAFFS_OK;\r
+ \r
+ dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);\r
+ \r
+ \r
+ \r
+ if(!init)\r
+ {\r
+ memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);\r
+ init = 1;\r
+ }\r
+ \r
+ if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) retVal = YAFFS_FAIL;\r
+ if(memcmp(cmpbuf,spare,16)) retVal = YAFFS_FAIL;\r
+ \r
+ return retVal;\r
+ \r
+}\r
+\r
+#endif\r
+\r
+\r
+int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)\r
+{\r
+ dev->nBlockErasures++;\r
+ return dev->eraseBlockInNAND(dev,blockInNAND);\r
+}\r
+\r
+int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)\r
+{\r
+ return dev->initialiseNAND(dev);\r
+}\r
+\r
+static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)\r
+{\r
+ int chunk;\r
+ \r
+ int writeOk = 0;\r
+ int attempts = 0;\r
+ \r
+ unsigned char rbData[YAFFS_BYTES_PER_CHUNK];\r
+ yaffs_Spare rbSpare;\r
+ \r
+ do{\r
+ chunk = yaffs_AllocateChunk(dev,useReserve);\r
+ \r
+ if(chunk >= 0)\r
+ {\r
+ writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare);\r
+ attempts++;\r
+ if(writeOk)\r
+ {\r
+ // Readback & verify\r
+ // If verify fails, then delete this chunk and try again\r
+ // To verify we compare everything except the block and \r
+ // page status bytes.\r
+ yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare);\r
+ \r
+ if(memcmp(data,rbData,YAFFS_BYTES_PER_CHUNK) != 0 ||\r
+ spare->tagByte0 != rbSpare.tagByte0 ||\r
+ spare->tagByte1 != rbSpare.tagByte1 ||\r
+ spare->tagByte2 != rbSpare.tagByte2 ||\r
+ spare->tagByte3 != rbSpare.tagByte3 ||\r
+ spare->tagByte4 != rbSpare.tagByte4 ||\r
+ spare->tagByte5 != rbSpare.tagByte5 ||\r
+ spare->tagByte6 != rbSpare.tagByte6 ||\r
+ spare->tagByte7 != rbSpare.tagByte7 ||\r
+ spare->ecc1[0] != rbSpare.ecc1[0] ||\r
+ spare->ecc1[1] != rbSpare.ecc1[1] ||\r
+ spare->ecc1[2] != rbSpare.ecc1[2] ||\r
+ spare->ecc2[0] != rbSpare.ecc2[0] ||\r
+ spare->ecc2[1] != rbSpare.ecc2[1] ||\r
+ spare->ecc2[2] != rbSpare.ecc2[2] )\r
+ {\r
+ // Didn't verify\r
+ yaffs_DeleteChunk(dev,chunk);\r
+ T((TSTR("**>> yaffs write failed on chunk %d" TENDSTR), chunk));\r
+\r
+ writeOk = 0;\r
+ } \r
+ \r
+ }\r
+ }\r
+ } while(chunk >= 0 && ! writeOk);\r
+ \r
+ if(attempts > 1)\r
+ {\r
+ T((TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));\r
+ dev->nRetriedWrites+= (attempts - 1); \r
+ }\r
+ \r
+ return chunk;\r
+}\r
+\r
+\r
+\r
+\r
+///////////////////////// Object management //////////////////\r
+// List of spare objects\r
+// The list is hooked together using the first pointer\r
+// in the object\r
+\r
+// static yaffs_Object *yaffs_freeObjects = NULL;\r
+\r
+// static int yaffs_nFreeObjects;\r
+\r
+// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;\r
+\r
+// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];\r
+\r
+\r
+static __u16 yaffs_CalcNameSum(const char *name)\r
+{\r
+ __u16 sum = 0;\r
+ __u16 i = 1;\r
+ \r
+ __u8 *bname = (__u8 *)name;\r
+ \r
+ while (*bname)\r
+ {\r
+ sum += (*bname) * i;\r
+ i++;\r
+ bname++;\r
+ }\r
+ return sum;\r
+}\r
+\r
+\r
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)\r
+{\r
+ nand_calculate_ecc (data , spare->ecc1);\r
+ nand_calculate_ecc (&data[256] , spare->ecc2);\r
+}\r
+\r
+void yaffs_CalcTagsECC(yaffs_Tags *tags)\r
+{\r
+ // Calculate an ecc\r
+ \r
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;\r
+ unsigned i,j;\r
+ unsigned ecc = 0;\r
+ unsigned bit = 0;\r
+\r
+ tags->ecc = 0;\r
+ \r
+ for(i = 0; i < 8; i++)\r
+ {\r
+ for(j = 1; j &0x7f; j<<=1)\r
+ {\r
+ bit++;\r
+ if(b[i] & j)\r
+ {\r
+ ecc ^= bit;\r
+ }\r
+ }\r
+ }\r
+ \r
+ tags->ecc = ecc;\r
+ \r
+ \r
+}\r
+\r
+void yaffs_CheckECCOnTags(yaffs_Tags *tags)\r
+{\r
+ unsigned ecc = tags->ecc;\r
+ \r
+ yaffs_CalcTagsECC(tags);\r
+ \r
+ ecc ^= tags->ecc;\r
+ \r
+ if(ecc)\r
+ {\r
+ // Needs fixing\r
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;\r
+\r
+ ecc--;\r
+ \r
+ b[ecc / 8] ^= (1 << (ecc & 7));\r
+ \r
+ // Now recvalc the ecc\r
+ yaffs_CalcTagsECC(tags);\r
+ }\r
+}\r
+\r
+\r
+///////////////////////// TNODES ///////////////////////\r
+\r
+// List of spare tnodes\r
+// The list is hooked together using the first pointer\r
+// in the tnode.\r
+\r
+//static yaffs_Tnode *yaffs_freeTnodes = NULL;\r
+\r
+// static int yaffs_nFreeTnodes;\r
+\r
+//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;\r
+\r
+\r
+\r
+// yaffs_CreateTnodes creates a bunch more tnodes and\r
+// adds them to the tnode free list.\r
+// Don't use this function directly\r
+\r
+static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)\r
+{\r
+ int i;\r
+ yaffs_Tnode *newTnodes;\r
+ yaffs_TnodeList *tnl;\r
+ \r
+ if(nTnodes < 1) return YAFFS_OK;\r
+ \r
+ // make these things\r
+ \r
+ newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));\r
+ \r
+ if (!newTnodes)\r
+ {\r
+ YALERT("Could not malloc tnodes");\r
+ return YAFFS_FAIL;\r
+ }\r
+ \r
+ // Hook them into the free list\r
+ for(i = 0; i < nTnodes - 1; i++)\r
+ {\r
+ newTnodes[i].internal[0] = &newTnodes[i+1];\r
+ }\r
+ \r
+ newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;\r
+ dev->freeTnodes = newTnodes;\r
+ dev->nFreeTnodes+= nTnodes;\r
+ dev->nTnodesCreated += nTnodes;\r
+\r
+ // Now add this bunch of tnodes to a list for freeing up.\r
+\r
+ tnl = YMALLOC(sizeof(yaffs_TnodeList));\r
+ if(!tnl)\r
+ {\r
+ YALERT("Could not add tnodes to management list");\r
+ }\r
+ else\r
+ {\r
+ tnl->tnodes = newTnodes;\r
+ tnl->next = dev->allocatedTnodeList;\r
+ dev->allocatedTnodeList = tnl;\r
+ }\r
+\r
+\r
+ YINFO("Tnodes created");\r
+\r
+\r
+ return YAFFS_OK;\r
+}\r
+\r
+\r
+// GetTnode gets us a clean tnode. Tries to make allocate more if we run out\r
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)\r
+{\r
+ yaffs_Tnode *tn = NULL;\r
+ \r
+ // If there are none left make more\r
+ if(!dev->freeTnodes)\r
+ {\r
+ yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);\r
+ }\r
+ \r
+ if(dev->freeTnodes)\r
+ {\r
+ tn = dev->freeTnodes;\r
+ dev->freeTnodes = dev->freeTnodes->internal[0];\r
+ dev->nFreeTnodes--;\r
+ // zero out\r
+ memset(tn,0,sizeof(yaffs_Tnode));\r
+ }\r
+ \r
+\r
+ return tn;\r
+}\r
+\r
+\r
+// FreeTnode frees up a tnode and puts it back on the free list\r
+static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)\r
+{\r
+ tn->internal[0] = dev->freeTnodes;\r
+ dev->freeTnodes = tn;\r
+ dev->nFreeTnodes++;\r
+}\r
+\r
+\r
+static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)\r
+{\r
+ // Free the list of allocated tnodes\r
+ \r
+ while(dev->allocatedTnodeList)\r
+ {\r
+ YFREE(dev->allocatedTnodeList->tnodes);\r
+ dev->allocatedTnodeList = dev->allocatedTnodeList->next;\r
+ }\r
+ \r
+ dev->freeTnodes = NULL;\r
+ dev->nFreeTnodes = 0;\r
+}\r
+\r
+static void yaffs_InitialiseTnodes(yaffs_Device*dev)\r
+{\r
+ dev->allocatedTnodeList = NULL;\r
+ dev->freeTnodes = NULL;\r
+ dev->nFreeTnodes = 0;\r
+ dev->nTnodesCreated = 0;\r
+\r
+}\r
+\r
+void yaffs_TnodeTest(yaffs_Device *dev)\r
+{\r
+\r
+ int i;\r
+ int j;\r
+ yaffs_Tnode *tn[1000];\r
+ \r
+ YINFO("Testing TNodes");\r
+ \r
+ for(j = 0; j < 50; j++)\r
+ {\r
+ for(i = 0; i < 1000; i++)\r
+ {\r
+ tn[i] = yaffs_GetTnode(dev);\r
+ if(!tn[i])\r
+ {\r
+ YALERT("Getting tnode failed");\r
+ }\r
+ }\r
+ for(i = 0; i < 1000; i+=3)\r
+ {\r
+ yaffs_FreeTnode(dev,tn[i]);\r
+ tn[i] = NULL;\r
+ }\r
+ \r
+ }\r
+}\r
+\r
+////////////////// END OF TNODE MANIPULATION ///////////////////////////\r
+\r
+/////////////// Functions to manipulate the look-up tree (made up of tnodes)\r
+// The look up tree is represented by the top tnode and the number of topLevel\r
+// in the tree. 0 means only the level 0 tnode is in the tree.\r
+\r
+\r
+// FindLevel0Tnode finds the level 0 tnode, if one exists.\r
+// Used when reading.....\r
+static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)\r
+{\r
+ \r
+ yaffs_Tnode *tn = fStruct->top;\r
+ __u32 i;\r
+ int requiredTallness; \r
+ int level = fStruct->topLevel;\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
+ 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
+ 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
+ requiredTallness = 0;\r
+ while(i)\r
+ {\r
+ i >>= YAFFS_TNODES_INTERNAL_BITS;\r
+ requiredTallness++;\r
+ }\r
+ \r
+ \r
+ if(requiredTallness > fStruct->topLevel)\r
+ {\r
+ // Not tall enough, so we can't find it, return NULL.\r
+ return NULL;\r
+ }\r
+ \r
+ \r
+ // Traverse down to level 0\r
+ while (level > 0 && tn)\r
+ {\r
+ tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) & \r
+ YAFFS_TNODES_INTERNAL_MASK]; \r
+ level--;\r
+ \r
+ }\r
+ \r
+ return tn; \r
+}\r
+\r
+// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.\r
+// This happens in two steps:\r
+// 1. If the tree isn't tall enough, then make it taller.\r
+// 2. Scan down the tree towards the level 0 tnode adding tnodes if required.\r
+//\r
+// Used when modifying the tree.\r
+//\r
+static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)\r
+{\r
+ \r
+ yaffs_Tnode *tn; \r
+ \r
+ int requiredTallness;\r
+ \r
+ __u32 i;\r
+ __u32 l;\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
+ 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
+ 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
+ requiredTallness = 0;\r
+ while(i)\r
+ {\r
+ i >>= YAFFS_TNODES_INTERNAL_BITS;\r
+ requiredTallness++;\r
+ }\r
+ \r
+ //T((TSTR(" required=%d"),requiredTallness));\r
+ \r
+ \r
+ if(requiredTallness > fStruct->topLevel)\r
+ {\r
+ // Not tall enough,gotta make the tree taller\r
+ for(i = fStruct->topLevel; i < requiredTallness; i++)\r
+ {\r
+ //T((TSTR(" add new top")));\r
+ \r
+ tn = yaffs_GetTnode(dev);\r
+ \r
+ if(tn)\r
+ {\r
+ tn->internal[0] = fStruct->top;\r
+ fStruct->top = tn;\r
+ }\r
+ else\r
+ {\r
+ YALERT("No more tnodes");\r
+ }\r
+ }\r
+ \r
+ fStruct->topLevel = requiredTallness;\r
+ }\r
+ \r
+ \r
+ // Traverse down to level 0, adding anything we need\r
+ \r
+ l = fStruct->topLevel;\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
+ YAFFS_TNODES_INTERNAL_MASK;\r
+ \r
+ //T((TSTR(" [%d:%d]"),l,i));\r
+ \r
+ if(!tn->internal[i])\r
+ {\r
+ //T((TSTR(" added")));\r
+ \r
+ tn->internal[i] = yaffs_GetTnode(dev);\r
+ }\r
+ \r
+ tn = tn->internal[i];\r
+ l--;\r
+ \r
+ }\r
+ \r
+ //TSTR(TENDSTR)));\r
+ \r
+ return tn; \r
+}\r
+\r
+// DeleteWorker scans backwards through the tnode tree and delets all the\r
+// chunks and tnodes in the file\r
+\r
+static void yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)\r
+{\r
+ int i;\r
+ int chunkInInode;\r
+ int theChunk;\r
+ yaffs_Tags tags;\r
+ int found;\r
+ int chunkDeleted;\r
+ \r
+ \r
+ if(tn)\r
+ {\r
+ if(level > 0)\r
+ {\r
+ \r
+ for(i = YAFFS_NTNODES_INTERNAL -1; 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
+ }\r
+ \r
+ }\r
+ }\r
+ else if(level == 0)\r
+ {\r
+ for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0; i--)\r
+ {\r
+ if(tn->level0[i])\r
+ {\r
+ int j;\r
+ \r
+ chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i;\r
+ \r
+ theChunk = tn->level0[i] << in->myDev->chunkGroupBits;\r
+\r
+ // Now we need to search for it\r
+ for(j = 0,found = 0; theChunk && j < in->myDev->chunkGroupSize && !found; j++)\r
+ {\r
+ yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,&tags,&chunkDeleted);\r
+ if(yaffs_TagsMatch(&tags,in->objectId,chunkInInode,chunkDeleted))\r
+ {\r
+ // found it;\r
+ found = 1;\r
+ \r
+ }\r
+ else\r
+ {\r
+ theChunk++;\r
+ }\r
+ }\r
+ \r
+ if(found)\r
+ {\r
+ yaffs_DeleteChunk(in->myDev,theChunk);\r
+ \r
+ }\r
+ \r
+ tn->level0[i] = 0;\r
+ }\r
+ \r
+ }\r
+ \r
+ }\r
+ \r
+ }\r
+ \r
+}\r
+\r
+\r
+\r
+\r
+// Pruning removes any part of the file structure tree that is beyond the\r
+// bounds of the file (ie that does not point to chunks).\r
+//\r
+// A file should only get pruned when its size is reduced.\r
+//\r
+// Before pruning, the chunks must be pulled from the tree and the\r
+// level 0 tnode entries must be zeroed out.\r
+// Could also use this for file deletion, but that's probably better handled\r
+// by a special case.\r
+\r
+// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()\r
+\r
+static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)\r
+{\r
+ int i;\r
+ int hasData;\r
+ \r
+ if(tn)\r
+ {\r
+ hasData = 0;\r
+ \r
+ for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)\r
+ {\r
+ if(tn->internal[i] && level > 0)\r
+ {\r
+ tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);\r
+ }\r
+ \r
+ if(tn->internal[i])\r
+ {\r
+ hasData++;\r
+ }\r
+ }\r
+ \r
+ if(hasData == 0 && del0)\r
+ {\r
+ // Free and return NULL\r
+ \r
+ yaffs_FreeTnode(dev,tn);\r
+ tn = NULL;\r
+ }\r
+ \r
+ }\r
+\r
+ return tn;\r
+ \r
+}\r
+\r
+static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)\r
+{\r
+ int i;\r
+ int hasData;\r
+ int done = 0;\r
+ yaffs_Tnode *tn;\r
+ \r
+ if(fStruct->topLevel > 0)\r
+ {\r
+ fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);\r
+ \r
+ // Now we have a tree with all the non-zero branches NULL but the height\r
+ // is the same as it was.\r
+ // Let's see if we can trim internal tnodes to shorten the tree.\r
+ // We can do this if only the 0th element in the tnode is in use \r
+ // (ie all the non-zero are NULL)\r
+ \r
+ while(fStruct->topLevel && !done)\r
+ {\r
+ tn = fStruct->top;\r
+ \r
+ hasData = 0;\r
+ for(i = 1; i <YAFFS_NTNODES_INTERNAL; i++)\r
+ {\r
+ if(tn->internal[i])\r
+ {\r
+ hasData++;\r
+ }\r
+ }\r
+ \r
+ if(!hasData)\r
+ {\r
+ fStruct->top = tn->internal[0];\r
+ fStruct->topLevel--;\r
+ yaffs_FreeTnode(dev,tn);\r
+ }\r
+ else\r
+ {\r
+ done = 1;\r
+ }\r
+ }\r
+ }\r
+ \r
+ return YAFFS_OK;\r
+}\r
+\r
+\r
+\r
+\r
+/////////////////////// End of File Structure functions. /////////////////\r
+\r
+// yaffs_CreateFreeObjects creates a bunch more objects and\r
+// adds them to the object free list.\r
+static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)\r
+{\r
+ int i;\r
+ yaffs_Object *newObjects;\r
+ yaffs_ObjectList *list;\r
+ \r
+ if(nObjects < 1) return YAFFS_OK;\r
+ \r
+ // make these things\r
+ \r
+ newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));\r
+ \r
+ if (!newObjects)\r
+ {\r
+ YALERT("Could not allocate more objects");\r
+ return YAFFS_FAIL;\r
+ }\r
+ \r
+ // Hook them into the free list\r
+ for(i = 0; i < nObjects - 1; i++)\r
+ {\r
+ (yaffs_Object *)newObjects[i].siblings.next = &newObjects[i+1];\r
+ }\r
+ \r
+ newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;\r
+ dev->freeObjects = newObjects;\r
+ dev->nFreeObjects+= nObjects;\r
+ dev->nObjectsCreated+= nObjects;\r
+ \r
+ // Now add this bunch of Objects to a list for freeing up.\r
+ \r
+ list = YMALLOC(sizeof(yaffs_ObjectList));\r
+ if(!list)\r
+ {\r
+ YALERT("Could not add Objects to management list");\r
+ }\r
+ else\r
+ {\r
+ list->objects = newObjects;\r
+ list->next = dev->allocatedObjectList;\r
+ dev->allocatedObjectList = list;\r
+ }\r
+ \r
+ \r
+ YINFO("Objects created");\r
+ \r
+ \r
+ return YAFFS_OK;\r
+}\r
+\r
+\r
+// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out\r
+static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)\r
+{\r
+ yaffs_Object *tn = NULL;\r
+ \r
+ // If there are none left make more\r
+ if(!dev->freeObjects)\r
+ {\r
+ yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);\r
+ }\r
+ \r
+ if(dev->freeObjects)\r
+ {\r
+ tn = dev->freeObjects;\r
+ dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);\r
+ dev->nFreeObjects--;\r
+ \r
+ // Now sweeten it up...\r
+ \r
+ memset(tn,0,sizeof(yaffs_Object));\r
+ tn->chunkId = -1;\r
+ tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;\r
+ INIT_LIST_HEAD(&(tn->hardLinks));\r
+ INIT_LIST_HEAD(&(tn->hashLink));\r
+ INIT_LIST_HEAD(&tn->siblings);\r
+ \r
+ // Add it to the lost and found directory.\r
+ // NB Can't put root or lostNFound in lostNFound so\r
+ // check if lostNFound exists first\r
+ if(dev->lostNFoundDir)\r
+ {\r
+ yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn); \r
+ }\r
+ }\r
+ \r
+\r
+ return tn;\r
+}\r
+\r
+static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)\r
+{\r
+\r
+ yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY); \r
+ if(obj)\r
+ {\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->st_mode = mode;\r
+ obj->myDev = dev;\r
+ obj->chunkId = 0; // Not a valid chunk.\r
+ }\r
+ \r
+ return obj;\r
+ \r
+}\r
+\r
+\r
+static void yaffs_UnhashObject(yaffs_Object *tn)\r
+{\r
+ int bucket;\r
+ yaffs_Device *dev = tn->myDev;\r
+ \r
+ \r
+ // If it is still linked into the bucket list, free from the list\r
+ if(!list_empty(&tn->hashLink))\r
+ {\r
+ list_del_init(&tn->hashLink);\r
+ bucket = yaffs_HashFunction(tn->objectId);\r
+ dev->objectBucket[bucket].count--;\r
+ }\r
+ \r
+}\r
+\r
+\r
+// FreeObject frees up a Object and puts it back on the free list\r
+static void yaffs_FreeObject(yaffs_Object *tn)\r
+{\r
+\r
+ yaffs_Device *dev = tn->myDev;\r
+ \r
+ yaffs_UnhashObject(tn);\r
+ \r
+ // Link into the free list.\r
+ (yaffs_Object *)(tn->siblings.next) = dev->freeObjects;\r
+ dev->freeObjects = tn;\r
+ dev->nFreeObjects++;\r
+}\r
+\r
+\r
+\r
+\r
+static void yaffs_DeinitialiseObjects(yaffs_Device *dev)\r
+{\r
+ // Free the list of allocated Objects\r
+ \r
+ while( dev->allocatedObjectList)\r
+ {\r
+ YFREE(dev->allocatedObjectList->objects);\r
+ dev->allocatedObjectList = dev->allocatedObjectList->next;\r
+ }\r
+ \r
+ dev->freeObjects = NULL;\r
+ dev->nFreeObjects = 0;\r
+}\r
+\r
+static void yaffs_InitialiseObjects(yaffs_Device *dev)\r
+{\r
+ int i;\r
+ \r
+ dev->allocatedObjectList = NULL;\r
+ dev->freeObjects = NULL;\r
+ dev->nFreeObjects = 0;\r
+ \r
+ for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)\r
+ {\r
+ INIT_LIST_HEAD(&dev->objectBucket[i].list);\r
+ dev->objectBucket[i].count = 0; \r
+ }\r
+\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+int yaffs_FindNiceObjectBucket(yaffs_Device *dev)\r
+{\r
+ static int x = 0;\r
+ int i;\r
+ int l = 999;\r
+ int lowest = 999999;\r
+\r
+ \r
+ // First let's see if we can find one that's empty.\r
+ \r
+ for(i = 0; i < 10 && lowest > 0; i++)\r
+ {\r
+ x++;\r
+ x %= YAFFS_NOBJECT_BUCKETS;\r
+ if(dev->objectBucket[x].count < lowest)\r
+ {\r
+ lowest = dev->objectBucket[x].count;\r
+ l = x;\r
+ }\r
+ \r
+ }\r
+ \r
+ // If we didn't find an empty list, then try\r
+ // looking a bit further for a short one\r
+ \r
+ for(i = 0; i < 10 && lowest > 3; i++)\r
+ {\r
+ x++;\r
+ x %= YAFFS_NOBJECT_BUCKETS;\r
+ if(dev->objectBucket[x].count < lowest)\r
+ {\r
+ lowest = dev->objectBucket[x].count;\r
+ l = x;\r
+ }\r
+ \r
+ }\r
+ \r
+ return l;\r
+}\r
+\r
+static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)\r
+{\r
+ int bucket = yaffs_FindNiceObjectBucket(dev);\r
+ \r
+ // Now find an object value that has not already been taken\r
+ // by scanning the list.\r
+ \r
+ int found = 0;\r
+ struct list_head *i;\r
+ \r
+ int n = bucket;\r
+\r
+ //yaffs_CheckObjectHashSanity(); \r
+ \r
+ while(!found)\r
+ {\r
+ found = 1;\r
+ n += YAFFS_NOBJECT_BUCKETS;\r
+ if(1 ||dev->objectBucket[bucket].count > 0)\r
+ {\r
+ list_for_each(i,&dev->objectBucket[bucket].list)\r
+ {\r
+ // If there is already one in the list\r
+ if(list_entry(i, yaffs_Object,hashLink)->objectId == n)\r
+ {\r
+ found = 0;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);\r
+ \r
+ return n; \r
+}\r
+\r
+void yaffs_HashObject(yaffs_Object *in)\r
+{\r
+ int bucket = yaffs_HashFunction(in->objectId);\r
+ yaffs_Device *dev = in->myDev;\r
+ \r
+ if(!list_empty(&in->hashLink))\r
+ {\r
+ YINFO("!!!");\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
+{\r
+ int bucket = yaffs_HashFunction(number);\r
+ struct list_head *i;\r
+ yaffs_Object *in;\r
+ \r
+ list_for_each(i,&dev->objectBucket[bucket].list)\r
+ {\r
+ // Look if it is in the list\r
+ in = list_entry(i, yaffs_Object,hashLink);\r
+ if(in->objectId == number)\r
+ {\r
+ return in;\r
+ }\r
+ }\r
+ \r
+ return NULL;\r
+}\r
+\r
+\r
+\r
+yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)\r
+{\r
+ \r
+ yaffs_Object *theObject;\r
+\r
+ if(number < 0)\r
+ {\r
+ number = yaffs_CreateNewObjectNumber(dev);\r
+ }\r
+ \r
+ theObject = yaffs_AllocateEmptyObject(dev);\r
+ \r
+ if(theObject)\r
+ {\r
+ theObject->fake = 0;\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
+\r
+ switch(type)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_FILE: \r
+ theObject->variant.fileVariant.fileSize = 0;\r
+ theObject->variant.fileVariant.scannedFileSize = 0;\r
+ theObject->variant.fileVariant.topLevel = 0;\r
+ theObject->variant.fileVariant.top = yaffs_GetTnode(dev);\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_DIRECTORY:\r
+ INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_SYMLINK:\r
+ // No action required\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_HARDLINK:\r
+ // No action required\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_SPECIAL:\r
+ // No action required\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_UNKNOWN:\r
+ // todo this should not happen\r
+ break;\r
+ }\r
+ }\r
+ \r
+ return theObject;\r
+}\r
+\r
+yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)\r
+{\r
+ yaffs_Object *theObject = NULL;\r
+ \r
+ if(number > 0)\r
+ {\r
+ theObject = yaffs_FindObjectByNumber(dev,number);\r
+ }\r
+ \r
+ if(!theObject)\r
+ {\r
+ theObject = yaffs_CreateNewObject(dev,number,type);\r
+ }\r
+ \r
+ return theObject;\r
+\r
+}\r
+\r
+char *yaffs_CloneString(const char *str)\r
+{\r
+ char *newStr = NULL;\r
+ \r
+ if(str && *str)\r
+ {\r
+ newStr = YMALLOC(strlen(str) + 1);\r
+ strcpy(newStr,str);\r
+ }\r
+\r
+ return newStr;\r
+ \r
+}\r
+\r
+//\r
+// Mknod (create) a new object.\r
+// equivalentObject only has meaning for a hard link;\r
+// aliasString only has meaning for a sumlink.\r
+// rdev only has meaning for devices (a subset of special objects)\r
+yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,\r
+ yaffs_Object *parent,\r
+ const char *name, \r
+ __u32 mode,\r
+ __u32 uid,\r
+ __u32 gid,\r
+ yaffs_Object *equivalentObject,\r
+ const char *aliasString,\r
+ __u32 rdev)\r
+{\r
+ yaffs_Object *in;\r
+\r
+ yaffs_Device *dev = parent->myDev;\r
+ \r
+ // Check if the entry exists. If it does then fail the call since we don't want a dup.\r
+ if(yaffs_FindObjectByName(parent,name))\r
+ {\r
+ return NULL;\r
+ }\r
+ \r
+ in = yaffs_CreateNewObject(dev,-1,type);\r
+ \r
+ if(in)\r
+ {\r
+ in->chunkId = -1;\r
+ in->valid = 1;\r
+ in->variantType = type;\r
+\r
+ in->st_mode = mode;\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
+ in->nDataChunks = 0;\r
+\r
+ in->sum = yaffs_CalcNameSum(name);\r
+ in->dirty = 1;\r
+ \r
+ yaffs_AddObjectToDirectory(parent,in);\r
+ \r
+ in->myDev = parent->myDev;\r
+ \r
+ \r
+ switch(type)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_SYMLINK:\r
+ in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_HARDLINK:\r
+ in->variant.hardLinkVariant.equivalentObject = equivalentObject;\r
+ in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;\r
+ list_add(&in->hardLinks,&equivalentObject->hardLinks);\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_FILE: // do nothing\r
+ case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing\r
+ case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing\r
+ case YAFFS_OBJECT_TYPE_UNKNOWN:\r
+ break;\r
+ }\r
+\r
+ yaffs_UpdateObjectHeader(in,name);\r
+\r
+ }\r
+ \r
+ return in;\r
+}\r
+\r
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)\r
+{\r
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0);\r
+}\r
+\r
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)\r
+{\r
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0);\r
+}\r
+\r
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)\r
+{\r
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev);\r
+}\r
+\r
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)\r
+{\r
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0);\r
+}\r
+\r
+// NB yaffs_Link returns the object id of the equivalent object.\r
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject)\r
+{\r
+ // Get the real object in case we were fed a hard link as an equivalent object\r
+ equivalentObject = yaffs_GetEquivalentObject(equivalentObject);\r
+ \r
+ if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0))\r
+ {\r
+ return equivalentObject;\r
+ }\r
+ else\r
+ {\r
+ return NULL;\r
+ }\r
+ \r
+}\r
+\r
+\r
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName)\r
+{\r
+ //yaffs_Device *dev = obj->myDev;\r
+\r
+ if(newDir == NULL)\r
+ {\r
+ newDir = obj->parent; // use the old directory\r
+ }\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
+ {\r
+ obj->sum = yaffs_CalcNameSum(newName);\r
+ obj->dirty = 1;\r
+ yaffs_AddObjectToDirectory(newDir,obj);\r
+ \r
+ if(yaffs_UpdateObjectHeader(obj,newName) >= 0)\r
+ {\r
+ return YAFFS_OK;\r
+ }\r
+ }\r
+ \r
+ return YAFFS_FAIL;\r
+}\r
+\r
+int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)\r
+{\r
+ yaffs_Object *obj;\r
+ \r
+ obj = yaffs_FindObjectByName(oldDir,oldName);\r
+ if(obj && obj->renameAllowed)\r
+ {\r
+ return yaffs_ChangeObjectName(obj,newDir,newName);\r
+ }\r
+ return YAFFS_FAIL;\r
+}\r
+\r
+\r
+\r
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)\r
+{\r
+ // Scan the buckets and check that the lists \r
+ // have as many members as the count says there are\r
+ int bucket;\r
+ int countEm;\r
+ struct list_head *j;\r
+ int ok = YAFFS_OK;\r
+ \r
+ for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)\r
+ {\r
+ countEm = 0;\r
+ \r
+ list_for_each(j,&dev->objectBucket[bucket].list)\r
+ {\r
+ countEm++;\r
+ }\r
+ \r
+ if(countEm != dev->objectBucket[bucket].count)\r
+ {\r
+ YALERT("Inode hash inconsistency");\r
+ ok = YAFFS_FAIL;\r
+ }\r
+ }\r
+\r
+ return ok;\r
+}\r
+\r
+void yaffs_ObjectTest(yaffs_Device *dev)\r
+{\r
+ yaffs_Object *in[1000];\r
+ int inNo[1000];\r
+ yaffs_Object *inold[1000];\r
+ int i;\r
+ int j;\r
+ \r
+ memset(in,0,1000*sizeof(yaffs_Object *));\r
+ memset(inold,0,1000*sizeof(yaffs_Object *));\r
+ \r
+ yaffs_CheckObjectHashSanity(dev);\r
+ \r
+ for(j = 0; j < 10; j++)\r
+ {\r
+ //T(("%d\n",j));\r
+ \r
+ for(i = 0; i < 1000; i++)\r
+ {\r
+ in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);\r
+ if(!in[i])\r
+ {\r
+ YINFO("No more inodes");\r
+ }\r
+ else\r
+ {\r
+ inNo[i] = in[i]->objectId;\r
+ }\r
+ }\r
+ \r
+ for(i = 0; i < 1000; i++)\r
+ {\r
+ if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])\r
+ {\r
+ //T(("Differnce in look up test\n"));\r
+ }\r
+ else\r
+ {\r
+ // T(("Look up ok\n"));\r
+ }\r
+ }\r
+ \r
+ yaffs_CheckObjectHashSanity(dev);\r
+ \r
+ for(i = 0; i < 1000; i+=3)\r
+ {\r
+ yaffs_FreeObject(in[i]); \r
+ in[i] = NULL;\r
+ }\r
+ \r
+ \r
+ yaffs_CheckObjectHashSanity(dev);\r
+ }\r
+ \r
+}\r
+\r
+\r
+\r
+/////////////////////////// Block Management and Page Allocation ///////////////////\r
+\r
+\r
+static void yaffs_InitialiseBlocks(yaffs_Device *dev)\r
+{\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
+}\r
+\r
+static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)\r
+{\r
+ YFREE(dev->blockInfo);\r
+}\r
+\r
+// FindDiretiestBlock is used to select the dirtiest block (or close enough)\r
+// for garbage collection.\r
+\r
+static int yaffs_FindDirtiestBlock(yaffs_Device *dev)\r
+{\r
+\r
+ int b = dev->currentDirtyChecker;\r
+ \r
+ int i;\r
+ int dirtiest = -1;\r
+ int pagesInUse = 100; // silly big number\r
+ \r
+ for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)\r
+ {\r
+ b++;\r
+ if (b > dev->endBlock)\r
+ {\r
+ b = dev->startBlock;\r
+ }\r
+ \r
+ if(dev->blockInfo[b].blockState == YAFFS_BLOCK_STATE_FULL &&\r
+ (dev->blockInfo)[b].pagesInUse < pagesInUse)\r
+ {\r
+ dirtiest = b;\r
+ pagesInUse = (dev->blockInfo)[b].pagesInUse;\r
+ }\r
+ }\r
+ \r
+ dev->currentDirtyChecker = b;\r
+ \r
+ return dirtiest;\r
+}\r
+\r
+\r
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve)\r
+{\r
+ int i;\r
+ \r
+ if(useReserve && dev->nErasedBlocks < 1)\r
+ {\r
+ // Hoosterman we've got a problem.\r
+ // Can't get space to gc\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
+ {\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
+ return i;\r
+ }\r
+ }\r
+ \r
+ return -1; \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, erase it and mark as clean.\r
+ bi->blockState = YAFFS_BLOCK_STATE_DIRTY;\r
+ yaffs_EraseBlockInNAND(dev,blockNo);\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
+\r
+\r
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)\r
+{\r
+ int retVal;\r
+ \r
+ if(dev->allocationBlock < 0)\r
+ {\r
+ // Get next block to allocate off\r
+ dev->allocationBlock = yaffs_FindBlockForAllocation(dev,useReserve);\r
+ dev->allocationPage = 0;\r
+ }\r
+ \r
+ // Next page please....\r
+ if(dev->allocationBlock >= 0)\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
+\r
+ dev->allocationPage++;\r
+ \r
+ dev->nFreeChunks--;\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
+ 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
+ return retVal;\r
+ \r
+ }\r
+ T((TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));\r
+\r
+ return -1; \r
+}\r
+\r
+\r
+int yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)\r
+{\r
+ int oldChunk;\r
+ int newChunk;\r
+ __u32 mask;\r
+ \r
+ \r
+ yaffs_Spare spare;\r
+ yaffs_Tags tags;\r
+ __u8 buffer[YAFFS_BYTES_PER_CHUNK];\r
+ \r
+ yaffs_BlockInfo *bi = &dev->blockInfo[block];\r
+ \r
+ yaffs_Object *object;\r
+\r
+ //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits)); \r
+ \r
+ for(mask = 1,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK; \r
+ mask && bi->pageBits;\r
+ mask <<= 1, oldChunk++ )\r
+ {\r
+ if(bi->pageBits & mask)\r
+ {\r
+ \r
+ // This page is in use and needs to be copied off\r
+ \r
+ dev->nGCCopies++;\r
+ \r
+ //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));\r
+ \r
+ yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare);\r
+ \r
+ yaffs_GetTagsFromSpare(&spare,&tags);\r
+ tags.serialNumber++;\r
+ yaffs_LoadTagsIntoSpare(&spare,&tags);\r
+\r
+#if 0\r
+ newChunk = yaffs_AllocatePage(dev,1);\r
+ if(newChunk < 0)\r
+ {\r
+ return YAFFS_FAIL;\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
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
+\r
+ object = yaffs_FindObjectByNumber(dev,tags.objectId);\r
+ \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
+ }\r
+ \r
+ yaffs_DeleteChunk(dev,oldChunk); \r
+ \r
+ }\r
+ }\r
+\r
+ return YAFFS_OK;\r
+}\r
+\r
+int yaffs_CheckGarbageCollection(yaffs_Device *dev)\r
+{\r
+ int block;\r
+ \r
+ if(dev->garbageCollectionRequired)\r
+ {\r
+ dev->garbageCollectionRequired = 0;\r
+ block = yaffs_FindDirtiestBlock(dev);\r
+ if(block >= 0)\r
+ {\r
+ return yaffs_GarbageCollectBlock(dev,block);\r
+ } \r
+ else\r
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
+ }\r
+\r
+ return YAFFS_OK;\r
+}\r
+\r
+\r
+//////////////////////////// TAGS ///////////////////////////////////////\r
+\r
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)\r
+{\r
+ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;\r
+ \r
+ yaffs_CalcTagsECC(tagsPtr);\r
+ \r
+ sparePtr->tagByte0 = tu->asBytes[0];\r
+ sparePtr->tagByte1 = tu->asBytes[1];\r
+ sparePtr->tagByte2 = tu->asBytes[2];\r
+ sparePtr->tagByte3 = tu->asBytes[3];\r
+ sparePtr->tagByte4 = tu->asBytes[4];\r
+ sparePtr->tagByte5 = tu->asBytes[5];\r
+ sparePtr->tagByte6 = tu->asBytes[6];\r
+ sparePtr->tagByte7 = tu->asBytes[7];\r
+}\r
+\r
+static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)\r
+{\r
+ yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;\r
+\r
+ tu->asBytes[0]= sparePtr->tagByte0;\r
+ tu->asBytes[1]= sparePtr->tagByte1;\r
+ tu->asBytes[2]= sparePtr->tagByte2;\r
+ tu->asBytes[3]= sparePtr->tagByte3;\r
+ tu->asBytes[4]= sparePtr->tagByte4;\r
+ tu->asBytes[5]= sparePtr->tagByte5;\r
+ tu->asBytes[6]= sparePtr->tagByte6;\r
+ tu->asBytes[7]= sparePtr->tagByte7;\r
+ \r
+ yaffs_CheckECCOnTags(tagsPtr);\r
+}\r
+\r
+static void yaffs_SpareInitialise(yaffs_Spare *spare)\r
+{\r
+ memset(spare,0xFF,sizeof(yaffs_Spare));\r
+}\r
+\r
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted)\r
+{\r
+ if(tags)\r
+ {\r
+ yaffs_Spare spare;\r
+ if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare) == YAFFS_OK)\r
+ {\r
+ *chunkDeleted = (yaffs_countBits[spare.pageStatus] < 7) ? 1 : 0;\r
+ yaffs_GetTagsFromSpare(&spare,tags);\r
+ return YAFFS_OK;\r
+ }\r
+ else\r
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
+ }\r
+ \r
+ return YAFFS_OK;\r
+}\r
+\r
+#if 0\r
+static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags)\r
+{\r
+ // NB There must be tags, data is optional\r
+ // If there is data, then an ECC is calculated on it.\r
+ \r
+ yaffs_Spare spare;\r
+ \r
+ if(!tags)\r
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
+ \r
+ yaffs_SpareInitialise(&spare);\r
+ \r
+\r
+ if(buffer)\r
+ {\r
+ yaffs_CalcECC(buffer,&spare);\r
+ }\r
+ \r
+ yaffs_LoadTagsIntoSpare(&spare,tags);\r
+ \r
+ return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);\r
+ \r
+}\r
+#endif\r
+\r
+\r
+static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)\r
+{\r
+ // NB There must be tags, data is optional\r
+ // If there is data, then an ECC is calculated on it.\r
+ \r
+ yaffs_Spare spare;\r
+ \r
+ if(!tags)\r
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
+ \r
+ yaffs_SpareInitialise(&spare);\r
+ \r
+\r
+ if(buffer)\r
+ {\r
+ yaffs_CalcECC(buffer,&spare);\r
+ }\r
+ \r
+ yaffs_LoadTagsIntoSpare(&spare,tags);\r
+ \r
+ return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);\r
+ \r
+}\r
+\r
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted)\r
+{\r
+ return ( tags->chunkId == chunkInObject &&\r
+ tags->objectId == objectId &&\r
+ !chunkDeleted) ? 1 : 0;\r
+ \r
+}\r
+\r
+\r
+\r
+int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)\r
+{\r
+ //Get the Tnode, then get the level 0 offset chunk offset\r
+ yaffs_Tnode *tn; \r
+ int theChunk = -1;\r
+ yaffs_Tags localTags;\r
+ int i;\r
+ int found = 0;\r
+ int chunkDeleted;\r
+ \r
+ yaffs_Device *dev = in->myDev;\r
+ \r
+ \r
+ if(!tags)\r
+ {\r
+ // Passed a NULL, so use our own tags space\r
+ tags = &localTags;\r
+ }\r
+ \r
+ tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);\r
+ \r
+ if(tn)\r
+ {\r
+ theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;\r
+\r
+ // Now we need to do the shifting etc and search for it\r
+ for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)\r
+ {\r
+ yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);\r
+ if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))\r
+ {\r
+ // found it;\r
+ found = 1;\r
+ }\r
+ else\r
+ {\r
+ theChunk++;\r
+ }\r
+ }\r
+ }\r
+ return found ? theChunk : -1;\r
+}\r
+\r
+int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)\r
+{\r
+ //Get the Tnode, then get the level 0 offset chunk offset\r
+ yaffs_Tnode *tn; \r
+ int theChunk = -1;\r
+ yaffs_Tags localTags;\r
+ int i;\r
+ int found = 0;\r
+ yaffs_Device *dev = in->myDev;\r
+ int chunkDeleted;\r
+ \r
+ if(!tags)\r
+ {\r
+ // Passed a NULL, so use our own tags space\r
+ tags = &localTags;\r
+ }\r
+ \r
+ tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);\r
+ \r
+ if(tn)\r
+ {\r
+ \r
+ theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;\r
+ \r
+ // Now we need to do the shifting etc and search for it\r
+ for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)\r
+ {\r
+ yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);\r
+ if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))\r
+ {\r
+ // found it;\r
+ found = 1;\r
+ }\r
+ else\r
+ {\r
+ theChunk++;\r
+ }\r
+ }\r
+ \r
+ // Delete the entry in the filestructure\r
+ if(found)\r
+ {\r
+ tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ //T(("No level 0 found for %d\n", chunkInInode));\r
+ }\r
+ \r
+ if(!found)\r
+ {\r
+ //T(("Could not find %d to delete\n",chunkInInode));\r
+ }\r
+ return found ? theChunk : -1;\r
+}\r
+\r
+\r
+#if YAFFS_PARANOID\r
+\r
+static int yaffs_CheckFileSanity(yaffs_Object *in)\r
+{\r
+ int chunk;\r
+ int nChunks;\r
+ int fSize;\r
+ int failed = 0;\r
+ int objId;\r
+ yaffs_Tnode *tn;\r
+ yaffs_Tags localTags;\r
+ yaffs_Tags *tags = &localTags;\r
+ int theChunk;\r
+ int chunkDeleted;\r
+ \r
+ \r
+ if(in->variantType != YAFFS_OBJECT_TYPE_FILE)\r
+ {\r
+ //T(("Object not a file\n"));\r
+ return YAFFS_FAIL;\r
+ }\r
+ \r
+ objId = in->objectId;\r
+ fSize = in->variant.fileVariant.fileSize;\r
+ nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK;\r
+ \r
+ for(chunk = 1; chunk <= nChunks; chunk++)\r
+ {\r
+ tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);\r
+ \r
+ if(tn)\r
+ {\r
+ \r
+ theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;\r
+ \r
+\r
+ yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted);\r
+ if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted))\r
+ {\r
+ // found it;\r
+ \r
+ }\r
+ else\r
+ {\r
+ //T(("File problem file [%d,%d] NAND %d tags[%d,%d]\n",\r
+ // objId,chunk,theChunk,tags->chunkId,tags->objectId);\r
+ \r
+ failed = 1;\r
+ \r
+ }\r
+ \r
+ }\r
+ else\r
+ {\r
+ //T(("No level 0 found for %d\n", chunk));\r
+ }\r
+ }\r
+ \r
+ return failed ? YAFFS_FAIL : YAFFS_OK;\r
+}\r
+\r
+#endif\r
+\r
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)\r
+{\r
+ yaffs_Tnode *tn;\r
+ yaffs_Device *dev = in->myDev;\r
+ int existingChunk;\r
+ yaffs_Tags existingTags;\r
+ yaffs_Tags newTags;\r
+ unsigned existingSerial, newSerial;\r
+ \r
+ int newChunkDeleted;\r
+ \r
+ \r
+ tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);\r
+ if(!tn)\r
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
+\r
+ existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK]; \r
+ \r
+ if(inScan)\r
+ {\r
+ // If we're scanning then we need to test for duplicates\r
+ // NB This does not need to be efficient since it should only ever \r
+ // happen when the power fails during a write, then only one\r
+ // chunk should ever be affected.\r
+ \r
+ \r
+ if(existingChunk != 0)\r
+ {\r
+ // NB Right now existing chunk will not be real chunkId if the device >= 32MB\r
+ // thus we have to do a FindChunkInFile to get the real chunk id.\r
+ //\r
+ // We have a duplicate now we need to decide which one to use\r
+ // To do this we get both sets of tags and compare serial numbers.\r
+ yaffs_ReadChunkTagsFromNAND(dev,chunkInNAND, &newTags,&newChunkDeleted);\r
+ \r
+ \r
+ // Do a proper find\r
+ existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);\r
+\r
+ if(existingChunk <=0)\r
+ {\r
+ //Hoosterman - how did this happen?\r
+ // todo\r
+ }\r
+\r
+ \r
+ // NB The deleted flags should be false, otherwise the chunks will \r
+ // not be loaded during a scan\r
+ \r
+ newSerial = newTags.serialNumber;\r
+ existingSerial = existingTags.serialNumber;\r
+ \r
+ if( existingChunk <= 0 ||\r
+ ((existingSerial+1) & 3) == newSerial)\r
+ {\r
+ // Use new\r
+ // Delete the old one and drop through to update the tnode\r
+ yaffs_DeleteChunk(dev,existingChunk);\r
+ }\r
+ else\r
+ {\r
+ // Use existing.\r
+ // Delete the new one and return early so that the tnode isn't changed\r
+ yaffs_DeleteChunk(dev,chunkInNAND);\r
+ return YAFFS_OK;\r
+ }\r
+ }\r
+\r
+ }\r
+ \r
+ if(existingChunk == 0)\r
+ {\r
+ in->nDataChunks++;\r
+ }\r
+ \r
+ tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits);\r
+ \r
+ return YAFFS_OK;\r
+}\r
+\r
+\r
+\r
+int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)\r
+{\r
+ int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);\r
+ \r
+ if(chunkInNAND >= 0)\r
+ {\r
+ return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL);\r
+ }\r
+ else\r
+ {\r
+ return 0;\r
+ }\r
+\r
+}\r
+\r
+\r
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)\r
+{\r
+ int block = chunkId / YAFFS_CHUNKS_PER_BLOCK;\r
+ int page = chunkId % YAFFS_CHUNKS_PER_BLOCK;\r
+ yaffs_Spare spare;\r
+ \r
+ yaffs_SpareInitialise(&spare);\r
+ \r
+ spare.pageStatus = 0; // To mark it as deleted.\r
+\r
+ \r
+ yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);\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
+ {\r
+ dev->nFreeChunks++;\r
+\r
+ dev->blockInfo[block].pageBits &= ~(1 << page);\r
+ dev->blockInfo[block].pagesInUse--;\r
+ \r
+ if( dev->blockInfo[block].pagesInUse == 0 &&\r
+ dev->blockInfo[block].blockState == YAFFS_BLOCK_STATE_FULL)\r
+ {\r
+ yaffs_BlockBecameDirty(dev,block);\r
+ }\r
+\r
+ }\r
+ else\r
+ {\r
+ // T(("Bad news deleting chunk %d\n",chunkId));\r
+ }\r
+ \r
+}\r
+\r
+\r
+\r
+\r
+int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)\r
+{\r
+ // Find old chunk Need to do this to get serial number\r
+ // Write new one and patch into tree.\r
+ // Invalidate old tags.\r
+\r
+ int prevChunkId;\r
+ yaffs_Tags prevTags;\r
+ \r
+ int newChunkId;\r
+ yaffs_Tags newTags;\r
+\r
+ yaffs_Device *dev = in->myDev; \r
+\r
+ yaffs_CheckGarbageCollection(dev);\r
+\r
+ // Get the previous chunk at this location in the file if it exists\r
+ prevChunkId = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);\r
+ \r
+ // Set up new tags\r
+ newTags.chunkId = chunkInInode;\r
+ newTags.objectId = in->objectId;\r
+ newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;\r
+ newTags.byteCount = nBytes;\r
+ newTags.unusedStuff = 0xFFFFFFFF;\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
+ yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);\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
+#endif\r
+\r
+\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
+{\r
+\r
+ yaffs_Device *dev = in->myDev;\r
+ \r
+ int prevChunkId;\r
+ \r
+ int newChunkId;\r
+ yaffs_Tags newTags;\r
+ __u8 bufferNew[YAFFS_BYTES_PER_CHUNK];\r
+ __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];\r
+ \r
+ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;\r
+ yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;\r
+ \r
+ if(!in->fake)\r
+ {\r
+ \r
+ yaffs_CheckGarbageCollection(dev); \r
+ \r
+ memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK);\r
+ \r
+ prevChunkId = in->chunkId;\r
+ \r
+ if(prevChunkId >= 0)\r
+ {\r
+ yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL); \r
+ }\r
+\r
+ // Header data\r
+ oh->type = in->variantType;\r
+ \r
+ oh->st_mode = in->st_mode;\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
+ 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
+ { \r
+ memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);\r
+ }\r
+ \r
+ switch(in->variantType)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_UNKNOWN: \r
+ // Should not happen\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_FILE:\r
+ oh->fileSize = in->variant.fileVariant.fileSize;\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_HARDLINK:\r
+ oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_SPECIAL: \r
+ // Do nothing\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_DIRECTORY: \r
+ // Do nothing\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_SYMLINK:\r
+ strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);\r
+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;\r
+ break;\r
+ }\r
+\r
+ // Tags\r
+ in->serial++;\r
+ newTags.chunkId = 0;\r
+ newTags.objectId = in->objectId;\r
+ newTags.serialNumber = in->serial;\r
+ newTags.byteCount = 0xFFFFFFFF;\r
+ newTags.unusedStuff = 0xFFFFFFFF;\r
+ \r
+ yaffs_CalcTagsECC(&newTags);\r
+ \r
+ \r
+ \r
+#if 0\r
+ // Create new chunk in NAND\r
+ newChunkId = yaffs_AllocatePage(dev,1);\r
+ \r
+ if(newChunkId >= 0)\r
+ {\r
+\r
+ yaffs_WriteChunkWithTagsToNAND(dev,newChunkId,bufferNew,&newTags);\r
+ \r
+ in->chunkId = newChunkId; \r
+ \r
+ if(prevChunkId >= 0)\r
+ {\r
+ yaffs_DeleteChunk(dev,prevChunkId);\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
+ \r
+ if(prevChunkId >= 0)\r
+ {\r
+ yaffs_DeleteChunk(dev,prevChunkId);\r
+ }\r
+ \r
+ in->dirty = 0;\r
+ }\r
+ \r
+ return newChunkId;\r
+\r
+#endif\r
+ }\r
+ return 0;\r
+}\r
+\r
+\r
+\r
+///////////////////////// File read/write ///////////////////////////////\r
+// Read and write have very similar structures.\r
+// In general the read/write has three parts to it\r
+// * An incomplete chunk to start with (if the read/write is not chunk-aligned)\r
+// * Some complete chunks\r
+// * An incomplete chunk to end off with\r
+//\r
+// Curve-balls: the first chunk might also be the last chunk.\r
+\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
+ int nToCopy;\r
+ int n = nBytes;\r
+ int nDone = 0;\r
+ \r
+ while(n > 0)\r
+ {\r
+ chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1\r
+ start = offset % YAFFS_BYTES_PER_CHUNK;\r
+\r
+ // OK now check for the curveball where the start and end are in\r
+ // the same chunk. \r
+ if( (start + n) < YAFFS_BYTES_PER_CHUNK)\r
+ {\r
+ nToCopy = n;\r
+ }\r
+ else\r
+ {\r
+ nToCopy = YAFFS_BYTES_PER_CHUNK - start;\r
+ }\r
+ \r
+ if(nToCopy != 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...\r
+ \r
+ yaffs_ReadChunkDataFromObject(in,chunk,localBuffer); \r
+ memcpy(buffer,&localBuffer[start],nToCopy);\r
+ }\r
+ else\r
+ {\r
+ // A full chunk. Read directly into the supplied buffer.\r
+ yaffs_ReadChunkDataFromObject(in,chunk,buffer);\r
+ }\r
+ \r
+ n -= nToCopy;\r
+ offset += nToCopy;\r
+ buffer += nToCopy;\r
+ nDone += nToCopy;\r
+ \r
+ }\r
+ \r
+ return nDone;\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 nToCopy;\r
+ int n = nBytes;\r
+ int nDone = 0;\r
+ int nToWriteBack;\r
+ int endOfWrite = offset+nBytes;\r
+ int chunkWritten = 0;\r
+ \r
+ while(n > 0 && chunkWritten >= 0)\r
+ {\r
+ chunk = offset / YAFFS_BYTES_PER_CHUNK + 1;\r
+ start = offset % YAFFS_BYTES_PER_CHUNK;\r
+ \r
+\r
+ // OK now check for the curveball where the start and end are in\r
+ // the same chunk.\r
+ if( (start + n) < YAFFS_BYTES_PER_CHUNK)\r
+ {\r
+ nToCopy = n;\r
+ nToWriteBack = (start + n);\r
+ }\r
+ else\r
+ {\r
+ nToCopy = YAFFS_BYTES_PER_CHUNK - start;\r
+ nToWriteBack = YAFFS_BYTES_PER_CHUNK;\r
+ }\r
+ \r
+ if(nToCopy != 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
+ memcpy(&localBuffer[start],buffer,nToCopy);\r
+ \r
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);\r
+ \r
+ //T(("Write with readback to chunk %d %d\n",chunk,chunkWritten));\r
+ \r
+ }\r
+ else\r
+ {\r
+ // A full chunk. Write directly from the supplied buffer.\r
+ chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);\r
+ //T(("Write to chunk %d %d\n",chunk,chunkWritten));\r
+ }\r
+ \r
+ if(chunkWritten >= 0)\r
+ {\r
+ n -= nToCopy;\r
+ offset += nToCopy;\r
+ buffer += nToCopy;\r
+ nDone += nToCopy;\r
+ }\r
+ \r
+ }\r
+ \r
+ // Update file object\r
+ \r
+ if(endOfWrite > in->variant.fileVariant.fileSize)\r
+ {\r
+ in->variant.fileVariant.fileSize = endOfWrite;\r
+ }\r
+ \r
+ in->dirty = 1;\r
+ /*in->st_mtime = CURRENT_TIME; only update in flush*/\r
+ \r
+ return nDone;\r
+}\r
+\r
+\r
+int yaffs_ResizeFile(yaffs_Object *in, int newSize)\r
+{\r
+ int i;\r
+ int chunkId;\r
+ int oldFileSize = in->variant.fileVariant.fileSize;\r
+ int sizeOfPartialChunk = newSize % YAFFS_BYTES_PER_CHUNK;\r
+ \r
+ yaffs_Device *dev = in->myDev;\r
+ \r
+ __u8 localBuffer[YAFFS_BYTES_PER_CHUNK];\r
+ \r
+ if(in->variantType != YAFFS_OBJECT_TYPE_FILE)\r
+ {\r
+ return yaffs_GetFileSize(in);\r
+ }\r
+ \r
+ if(newSize < oldFileSize)\r
+ {\r
+ \r
+ int lastDel = 1 + (oldFileSize-1)/YAFFS_BYTES_PER_CHUNK;\r
+ \r
+ int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/\r
+ YAFFS_BYTES_PER_CHUNK;\r
+\r
+ // Delete backwards so that we don't end up with holes if\r
+ // power is lost part-way through the operation.\r
+ for(i = lastDel; i >= startDel; i--)\r
+ {\r
+ // NB this could be optimised somewhat,\r
+ // eg. could retrieve the tags and write them without\r
+ // using yaffs_DeleteChunk\r
+\r
+ chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);\r
+ if(chunkId <= 0 || chunkId >= (dev->endBlock * 32))\r
+ {\r
+ //T(("Found daft chunkId %d for %d\n",chunkId,i));\r
+ }\r
+ else\r
+ {\r
+ in->nDataChunks--;\r
+ yaffs_DeleteChunk(dev,chunkId);\r
+ }\r
+ }\r
+ \r
+ \r
+ if(sizeOfPartialChunk != 0)\r
+ {\r
+ int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;\r
+ \r
+ // Got to read and rewrite the last chunk with its new size.\r
+ yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);\r
+ \r
+ yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1);\r
+ \r
+ }\r
+ \r
+ in->variant.fileVariant.fileSize = newSize;\r
+ \r
+ yaffs_PruneFileStructure(dev,&in->variant.fileVariant);\r
+ \r
+ return newSize;\r
+ \r
+ }\r
+ else\r
+ {\r
+ return oldFileSize;\r
+ }\r
+}\r
+\r
+\r
+loff_t yaffs_GetFileSize(yaffs_Object *obj)\r
+{\r
+ obj = yaffs_GetEquivalentObject(obj);\r
+ \r
+ switch(obj->variantType)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_FILE: \r
+ return obj->variant.fileVariant.fileSize;\r
+ case YAFFS_OBJECT_TYPE_SYMLINK:\r
+ return strlen(obj->variant.symLinkVariant.alias);\r
+ default:\r
+ return 0;\r
+ }\r
+}\r
+\r
+\r
+\r
+// yaffs_FlushFile() updates the file's\r
+// objectId in NAND\r
+\r
+int yaffs_FlushFile(yaffs_Object *in)\r
+{\r
+ int retVal;\r
+ if(in->dirty)\r
+ {\r
+ //T(("flushing object header\n"));\r
+ \r
+ in->st_mtime = CURRENT_TIME;\r
+\r
+ retVal = yaffs_UpdateObjectHeader(in,NULL);\r
+ }\r
+ else\r
+ {\r
+ retVal = YAFFS_OK;\r
+ }\r
+ \r
+ return retVal;\r
+ \r
+}\r
+\r
+\r
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)\r
+{\r
+ yaffs_RemoveObjectFromDirectory(in);\r
+ yaffs_DeleteChunk(in->myDev,in->chunkId);\r
+ yaffs_FreeObject(in);\r
+ return YAFFS_OK;\r
+\r
+}\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
+{\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
+\r
+ yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);\r
+ \r
+ return yaffs_DoGenericObjectDeletion(in);\r
+}\r
+\r
+static int yaffs_DeleteDirectory(yaffs_Object *in)\r
+{\r
+ //First check that the directory is empty.\r
+ if(list_empty(&in->variant.directoryVariant.children))\r
+ {\r
+ return yaffs_DoGenericObjectDeletion(in);\r
+ }\r
+ \r
+ return YAFFS_FAIL;\r
+ \r
+}\r
+\r
+static int yaffs_DeleteSymLink(yaffs_Object *in)\r
+{\r
+ YFREE(in->variant.symLinkVariant.alias);\r
+\r
+ return yaffs_DoGenericObjectDeletion(in);\r
+}\r
+\r
+static int yaffs_DeleteHardLink(yaffs_Object *in)\r
+{\r
+ // remove this hardlink from the list assocaited with the equivalent\r
+ // object\r
+ list_del(&in->hardLinks);\r
+ return yaffs_DoGenericObjectDeletion(in); \r
+}\r
+\r
+\r
+static int yaffs_UnlinkWorker(yaffs_Object *obj)\r
+{\r
+\r
+ \r
+ if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)\r
+ {\r
+ return yaffs_DeleteHardLink(obj);\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
+ // The Linux link/inode model.\r
+ //\r
+ // We can't really delete the object.\r
+ // Instead, we do the following:\r
+ // - Select a hardlink.\r
+ // - Unhook it from the hard links\r
+ // - Unhook it from its parent directory (so that the rename can work)\r
+ // - Rename the object to the hardlink's name.\r
+ // - Delete the hardlink\r
+ \r
+ \r
+ yaffs_Object *hl;\r
+ int retVal;\r
+ char name[YAFFS_MAX_NAME_LENGTH+1];\r
+ \r
+ hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);\r
+ \r
+ list_del_init(&hl->hardLinks);\r
+ list_del_init(&hl->siblings);\r
+ \r
+ yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);\r
+ \r
+ retVal = yaffs_ChangeObjectName(obj, hl->parent, name);\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
+ {\r
+ switch(obj->variantType)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_FILE:\r
+ return yaffs_DeleteFile(obj);\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_DIRECTORY:\r
+ return yaffs_DeleteDirectory(obj);\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_SYMLINK:\r
+ return yaffs_DeleteSymLink(obj);\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_HARDLINK:\r
+ case YAFFS_OBJECT_TYPE_UNKNOWN:\r
+ default:\r
+ return YAFFS_FAIL;\r
+ }\r
+ }\r
+}\r
+\r
+int yaffs_Unlink(yaffs_Object *dir, const char *name)\r
+{\r
+ yaffs_Object *obj;\r
+ \r
+ obj = yaffs_FindObjectByName(dir,name);\r
+ \r
+ if(obj && obj->unlinkAllowed)\r
+ {\r
+ return yaffs_UnlinkWorker(obj);\r
+ }\r
+ \r
+ return YAFFS_FAIL;\r
+ \r
+}\r
+\r
+//////////////// Initialisation Scanning /////////////////\r
+\r
+\r
+\r
+// For now we use the SmartMedia check.\r
+// We look at the blockStatus byte in the first two chunks\r
+// These must be 0xFF to pass as OK.\r
+// todo: this function needs to be modifyable foir different NAND types\r
+// and different chunk sizes. Suggest make this into a per-device configurable\r
+// function.\r
+static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)\r
+{\r
+ yaffs_Spare spare;\r
+ \r
+ yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK,NULL,&spare);\r
+ if(spare.blockStatus != 0xFF)\r
+ {\r
+ return 1;\r
+ }\r
+ yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK + 1,NULL,&spare);\r
+ if(spare.blockStatus != 0xFF)\r
+ {\r
+ return 1;\r
+ }\r
+ \r
+ return 0;\r
+ \r
+}\r
+\r
+static int yaffs_Scan(yaffs_Device *dev)\r
+{\r
+ yaffs_Spare spare;\r
+ yaffs_Tags tags;\r
+ int blk;\r
+ int chunk;\r
+ int c;\r
+ int deleted;\r
+ yaffs_BlockState state;\r
+ yaffs_Object *hardList = NULL;\r
+ yaffs_Object *hl;\r
+ \r
+\r
+ \r
+ yaffs_ObjectHeader *oh;\r
+ yaffs_Object *in;\r
+ yaffs_Object *parent;\r
+ \r
+ __u8 chunkData[YAFFS_BYTES_PER_CHUNK];\r
+ \r
+ \r
+ // Scan all the blocks...\r
+ \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
+ 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
+ }\r
+ \r
+ // Read each chunk in the block.\r
+ \r
+ for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK && \r
+ state == YAFFS_BLOCK_STATE_SCANNING; c++)\r
+ {\r
+ // Read the spare area and decide what to do\r
+ chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c;\r
+ \r
+ yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare);\r
+\r
+ \r
+ // This block looks ok, now what's in this chunk?\r
+ yaffs_GetTagsFromSpare(&spare,&tags);\r
+ \r
+ if(yaffs_countBits[spare.pageStatus] < 6)\r
+ {\r
+ // A deleted chunk\r
+ deleted++;\r
+ dev->nFreeChunks ++;\r
+ //T((" %d %d deleted\n",blk,c));\r
+ }\r
+ else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)\r
+ {\r
+ // An unassigned chunk in the block\r
+ // This means that either the block is empty or \r
+ // this is the one being allocated from\r
+ \r
+ if(c == 0)\r
+ {\r
+ // the block is unused\r
+ state = YAFFS_BLOCK_STATE_EMPTY;\r
+ dev->nErasedBlocks++;\r
+ }\r
+ else\r
+ {\r
+ // this is the block being allocated from\r
+ T((TSTR(" Allocating from %d %d" TENDSTR),blk,c));\r
+ state = YAFFS_BLOCK_STATE_ALLOCATING;\r
+ dev->allocationBlock = blk;\r
+ dev->allocationPage = c;\r
+ }\r
+\r
+ dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);\r
+ }\r
+ else if(tags.chunkId > 0)\r
+ {\r
+ int endpos;\r
+ // A data chunk.\r
+ dev->blockInfo[blk].pageBits |= (1 << c);\r
+ dev->blockInfo[blk].pagesInUse++;\r
+ \r
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);\r
+ // PutChunkIntoFIle checks for a clash (two data chunks with\r
+ // the same chunkId).\r
+ yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);\r
+ endpos = (tags.chunkId - 1)* YAFFS_BYTES_PER_CHUNK + tags.byteCount;\r
+ if(in->variant.fileVariant.scannedFileSize <endpos)\r
+ {\r
+ in->variant.fileVariant.scannedFileSize = endpos;\r
+ }\r
+ //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); \r
+ }\r
+ else\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
+ \r
+ yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL);\r
+ \r
+ oh = (yaffs_ObjectHeader *)chunkData;\r
+ \r
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);\r
+ \r
+ if(in->valid)\r
+ {\r
+ // We have already filled this one. We have a duplicate and need to resolve it.\r
+ \r
+ unsigned existingSerial = in->serial;\r
+ unsigned newSerial = tags.serialNumber;\r
+ \r
+ if(((existingSerial+1) & 3) == newSerial)\r
+ {\r
+ // Use new one - destroy the exisiting one\r
+ yaffs_DeleteChunk(dev,in->chunkId);\r
+ in->valid = 0;\r
+ }\r
+ else\r
+ {\r
+ // Use existing - destroy this one.\r
+ yaffs_DeleteChunk(dev,chunk);\r
+ }\r
+ }\r
+ \r
+ if(!in->valid)\r
+ {\r
+ // we need to load this info\r
+ \r
+ in->valid = 1;\r
+ in->variantType = oh->type;\r
+ \r
+ in->st_mode = oh->st_mode;\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
+ in->chunkId = chunk;\r
+\r
+ in->sum = oh->sum;\r
+ in->dirty = 0;\r
+ \r
+ // directory stuff...\r
+ // hook up to parent\r
+ \r
+ parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);\r
+ if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)\r
+ {\r
+ // Set up as a directory\r
+ parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;\r
+ INIT_LIST_HEAD(&parent->variant.directoryVariant.children);\r
+ }\r
+ else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)\r
+ {\r
+ // Hoosterman, another problem....\r
+ // We're trying to use a non-directory as a directory\r
+ // Todo ... handle\r
+ }\r
+ \r
+ yaffs_AddObjectToDirectory(parent,in); \r
+ \r
+ // Note re hardlinks.\r
+ // Since we might scan a hardlink before its equivalent object is scanned\r
+ // we put them all in a list.\r
+ // After scanning is complete, we should have all the objects, so we run through this\r
+ // list and fix up all the chains. \r
+ \r
+ switch(in->variantType)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_FILE:\r
+ in->variant.fileVariant.fileSize = oh->fileSize;\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_HARDLINK:\r
+ in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;\r
+ (yaffs_Object *)(in->hardLinks.next) = hardList;\r
+ hardList = in;\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing\r
+ in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);\r
+ break;\r
+ }\r
+ //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType)); \r
+ }\r
+ }\r
+ }\r
+ \r
+ if(state == YAFFS_BLOCK_STATE_SCANNING)\r
+ {\r
+ // If we got this far while scanning, then the block is fully allocated.\r
+ state = YAFFS_BLOCK_STATE_FULL; \r
+ }\r
+ \r
+ dev->blockInfo[blk].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
+ {\r
+ yaffs_BlockBecameDirty(dev,blk);\r
+ }\r
+\r
+ }\r
+ \r
+ // Fix up the hard link chains.\r
+ // We should now have scanned all the objects, now it's time to add these \r
+ // hardlinks.\r
+ while(hardList)\r
+ {\r
+ hl = hardList;\r
+ hardList = (yaffs_Object *)(hardList->hardLinks.next);\r
+ \r
+ in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);\r
+ \r
+ if(in)\r
+ {\r
+ // Add the hardlink pointers\r
+ hl->variant.hardLinkVariant.equivalentObject=in;\r
+ list_add(&hl->hardLinks,&in->hardLinks);\r
+ }\r
+ else\r
+ {\r
+ //Todo Need to report/handle this better.\r
+ // Got a problem... hardlink to a non-existant object\r
+ hl->variant.hardLinkVariant.equivalentObject=NULL;\r
+ INIT_LIST_HEAD(&hl->hardLinks);\r
+ \r
+ }\r
+ \r
+ }\r
+ \r
+ \r
+ \r
+ return YAFFS_OK;\r
+}\r
+\r
+\r
+////////////////////////// Directory Functions /////////////////////////\r
+\r
+\r
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)\r
+{\r
+\r
+ if(obj->siblings.prev == NULL)\r
+ {\r
+ // Not initialised\r
+ INIT_LIST_HEAD(&obj->siblings);\r
+ \r
+ }\r
+ else if(!list_empty(&obj->siblings))\r
+ {\r
+ // If it is holed up somewhere else, un hook it\r
+ list_del_init(&obj->siblings);\r
+ }\r
+ // Now add it\r
+ list_add(&obj->siblings,&directory->variant.directoryVariant.children);\r
+ obj->parent = directory;\r
+}\r
+\r
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)\r
+{\r
+ list_del_init(&obj->siblings);\r
+ obj->parent = NULL;\r
+}\r
+\r
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)\r
+{\r
+ int sum;\r
+ \r
+ struct list_head *i;\r
+ char buffer[YAFFS_MAX_NAME_LENGTH+1];\r
+ \r
+ yaffs_Object *l;\r
+ \r
+ sum = yaffs_CalcNameSum(name);\r
+ \r
+ list_for_each(i,&directory->variant.directoryVariant.children)\r
+ {\r
+ l = list_entry(i, yaffs_Object,siblings);\r
+ \r
+ // Special case for lost-n-found\r
+ if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)\r
+ {\r
+ if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)\r
+ {\r
+ return l;\r
+ }\r
+ }\r
+ else if(yaffs_SumCompare(l->sum, sum))\r
+ {\r
+ // Do a real check\r
+ yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);\r
+ if(yaffs_strcmp(name,buffer) == 0)\r
+ {\r
+ return l;\r
+ }\r
+ \r
+ \r
+ }\r
+ }\r
+ \r
+ return NULL;\r
+}\r
+\r
+\r
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *))\r
+{\r
+ struct list_head *i; \r
+ yaffs_Object *l;\r
+ \r
+ \r
+ list_for_each(i,&theDir->variant.directoryVariant.children)\r
+ {\r
+ l = list_entry(i, yaffs_Object,siblings);\r
+ if(!fn(l))\r
+ {\r
+ return YAFFS_FAIL;\r
+ }\r
+ }\r
+ \r
+ return YAFFS_OK;\r
+\r
+}\r
+\r
+\r
+// GetEquivalentObject dereferences any hard links to get to the\r
+// actual object.\r
+\r
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)\r
+{\r
+ if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)\r
+ {\r
+ // We want the object id of the equivalent object, not this one\r
+ obj = obj->variant.hardLinkVariant.equivalentObject;\r
+ }\r
+ return obj;\r
+\r
+}\r
+\r
+int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize)\r
+{\r
+ memset(name,0,buffSize);\r
+ \r
+ if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)\r
+ {\r
+ strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);\r
+ }\r
+ else if(obj->chunkId <= 0)\r
+ {\r
+ char locName[20];\r
+ // make up a name\r
+ sprintf(locName,"%s%d",YAFFS_LOSTNFOUND_PREFIX,obj->objectId);\r
+ strncpy(name,locName,buffSize - 1);\r
+\r
+ }\r
+ else\r
+ {\r
+ __u8 buffer[YAFFS_BYTES_PER_CHUNK];\r
+ yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;\r
+\r
+ memset(buffer,0,YAFFS_BYTES_PER_CHUNK);\r
+ \r
+ if(obj->chunkId >= 0)\r
+ {\r
+ yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);\r
+ }\r
+ strncpy(name,oh->name,buffSize - 1);\r
+ }\r
+ \r
+ return strlen(name);\r
+}\r
+\r
+int yaffs_GetObjectFileLength(yaffs_Object *obj)\r
+{\r
+ \r
+ // Dereference any hard linking\r
+ obj = yaffs_GetEquivalentObject(obj);\r
+ \r
+ if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)\r
+ {\r
+ return obj->variant.fileVariant.fileSize;\r
+ }\r
+ if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)\r
+ {\r
+ return strlen(obj->variant.symLinkVariant.alias);\r
+ }\r
+ else\r
+ {\r
+ // Only a directory should drop through to here\r
+ return YAFFS_BYTES_PER_CHUNK;\r
+ } \r
+}\r
+\r
+int yaffs_GetObjectLinkCount(yaffs_Object *obj)\r
+{\r
+ int count = 1; // the object itself\r
+ struct list_head *i;\r
+ \r
+ list_for_each(i,&obj->hardLinks)\r
+ {\r
+ count++;\r
+ }\r
+ return count;\r
+ \r
+}\r
+\r
+\r
+int yaffs_GetObjectInode(yaffs_Object *obj)\r
+{\r
+ obj = yaffs_GetEquivalentObject(obj);\r
+ \r
+ return obj->objectId;\r
+}\r
+\r
+unsigned yaffs_GetObjectType(yaffs_Object *obj)\r
+{\r
+ obj = yaffs_GetEquivalentObject(obj);\r
+ \r
+ switch(obj->variantType)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_FILE: return DT_REG; break;\r
+ case YAFFS_OBJECT_TYPE_DIRECTORY: return DT_DIR; break;\r
+ case YAFFS_OBJECT_TYPE_SYMLINK: return DT_LNK; break;\r
+ case YAFFS_OBJECT_TYPE_HARDLINK: return DT_REG; break;\r
+ case YAFFS_OBJECT_TYPE_SPECIAL: \r
+ if(S_ISFIFO(obj->st_mode)) return DT_FIFO;\r
+ if(S_ISCHR(obj->st_mode)) return DT_CHR;\r
+ if(S_ISBLK(obj->st_mode)) return DT_BLK;\r
+ if(S_ISSOCK(obj->st_mode)) return DT_SOCK;\r
+ default: return DT_REG; break;\r
+ }\r
+}\r
+\r
+char *yaffs_GetSymlinkAlias(yaffs_Object *obj)\r
+{\r
+ obj = yaffs_GetEquivalentObject(obj);\r
+ if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)\r
+ {\r
+ return yaffs_CloneString(obj->variant.symLinkVariant.alias);\r
+ }\r
+ else\r
+ {\r
+ return yaffs_CloneString("");\r
+ }\r
+}\r
+\r
+\r
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)\r
+{\r
+ unsigned int valid = attr->ia_valid;\r
+ \r
+ if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode;\r
+ if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;\r
+ if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;\r
+ \r
+ if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime;\r
+ if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime;\r
+ if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime;\r
+ \r
+ if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);\r
+ \r
+ yaffs_UpdateObjectHeader(obj,NULL);\r
+ \r
+ return YAFFS_OK;\r
+ \r
+}\r
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)\r
+{\r
+ unsigned int valid = 0;\r
+ \r
+ attr->ia_mode = obj->st_mode; valid |= ATTR_MODE;\r
+ attr->ia_uid = obj->st_uid; valid |= ATTR_UID;\r
+ attr->ia_gid = obj->st_gid; valid |= ATTR_GID;\r
+ \r
+ attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME;\r
+ attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME;\r
+ attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME;\r
+ \r
+ attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;\r
+ \r
+ attr->ia_valid = valid;\r
+ \r
+ return YAFFS_OK;\r
+ \r
+}\r
+\r
+\r
+\r
+int yaffs_DumpObject(yaffs_Object *obj)\r
+{\r
+// __u8 buffer[YAFFS_BYTES_PER_CHUNK];\r
+ char name[257];\r
+// yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;\r
+\r
+// memset(buffer,0,YAFFS_BYTES_PER_CHUNK);\r
+ \r
+// if(obj->chunkId >= 0)\r
+// {\r
+// yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);\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
+ 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
+#if 0\r
+ YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n",\r
+ obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial, \r
+ obj->sum, obj->chunkId));\r
+ switch(obj->variantType)\r
+ {\r
+ case YAFFS_OBJECT_TYPE_FILE: \r
+ YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize));\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_DIRECTORY:\r
+ YPRINTF((" DIRECTORY\n"));\r
+ break;\r
+ case YAFFS_OBJECT_TYPE_HARDLINK: //todo\r
+ case YAFFS_OBJECT_TYPE_SYMLINK:\r
+ case YAFFS_OBJECT_TYPE_UNKNOWN:\r
+ default:\r
+ }\r
+#endif\r
+ \r
+ return YAFFS_OK;\r
+}\r
+\r
+\r
+///////////////////////// Initialisation code ///////////////////////////\r
+\r
+\r
+\r
+int yaffs_GutsInitialise(yaffs_Device *dev)\r
+{\r
+ unsigned nChunks,x;\r
+ int bits;\r
+\r
+\r
+ dev = dev;\r
+ \r
+ if(!yaffs_CheckStructures())\r
+ {\r
+ return YAFFS_FAIL;\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
+ \r
+ for(bits = 0, x = nChunks; (x & 1) == 0; bits++)\r
+ {\r
+ x >>= 1;\r
+ }\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
+ 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/0x10000;\r
+ }\r
+ \r
+ // More device initialisation\r
+ dev->garbageCollectionRequired = 0;\r
+ dev->currentDirtyChecker = 0;\r
+ \r
+ yaffs_InitialiseBlocks(dev);\r
+ \r
+ yaffs_InitialiseTnodes(dev);\r
+\r
+ yaffs_InitialiseObjects(dev);\r
+ \r
+ \r
+ // Initialise the root and lost and found directories\r
+ dev->lostNFoundDir = dev->rootDir = NULL;\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
+ yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);\r
+ \r
+ // Now scan the flash. \r
+ yaffs_Scan(dev);\r
+ \r
+ // Zero out stats\r
+ dev->nPageReads = 0;\r
+ dev->nPageWrites = 0;\r
+ dev->nBlockErasures = 0;\r
+ dev->nGCCopies = 0;\r
+ dev->nRetriedWrites = 0;\r
+\r
+ \r
+ return YAFFS_OK;\r
+ \r
+}\r
+\r
+void yaffs_Deinitialise(yaffs_Device *dev)\r
+{\r
+ yaffs_DeinitialiseBlocks(dev);\r
+ yaffs_DeinitialiseTnodes(dev);\r
+ yaffs_DeinitialiseObjects(dev);\r
+ \r
+}\r
+\r
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)\r
+{\r
+ int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);\r
+\r
+ return (nFree < 0) ? 0 : nFree; \r
+ \r
+}\r
+\r
+\r
+/////////////////// YAFFS test code //////////////////////////////////\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
+ return YAFFS_FAIL; \\r
+ }\r
+ \r
+ \r
+static int yaffs_CheckStructures(void)\r
+{\r
+ yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")\r
+ yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")\r
+ yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")\r
+ yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")\r
+ yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")\r
+ \r
+ \r
+ return YAFFS_OK;\r
+}\r
+\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
+ return;\r
+ }\r
+ else\r
+ {\r
+ YPRINTF(("Structures OK\n"));\r
+ }\r
+ \r
+ yaffs_TnodeTest(dev);\r
+ yaffs_ObjectTest(dev); \r
+}\r
+\r
+\r