+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 ylist_head *i;
+
+ __u32 n = (__u32) bucket;
+
+ /* yaffs_CheckObjectHashSanity(); */
+
+ while (!found) {
+ found = 1;
+ n += YAFFS_NOBJECT_BUCKETS;
+ if (1 || dev->objectBucket[bucket].count > 0) {
+ ylist_for_each(i, &dev->objectBucket[bucket].list) {
+ /* If there is already one in the list */
+ if (i && ylist_entry(i, yaffs_Object,
+ hashLink)->objectId == n) {
+ found = 0;
+ }
+ }
+ }
+ }
+
+ return n;
+}
+
+static void yaffs_HashObject(yaffs_Object *in)
+{
+ int bucket = yaffs_HashFunction(in->objectId);
+ yaffs_Device *dev = in->myDev;
+
+ ylist_add(&in->hashLink, &dev->objectBucket[bucket].list);
+ dev->objectBucket[bucket].count++;
+}
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev, __u32 number)
+{
+ int bucket = yaffs_HashFunction(number);
+ struct ylist_head *i;
+ yaffs_Object *in;
+
+ ylist_for_each(i, &dev->objectBucket[bucket].list) {
+ /* Look if it is in the list */
+ if (i) {
+ in = ylist_entry(i, yaffs_Object, hashLink);
+ if (in->objectId == number) {
+
+ /* Don't tell the VFS about this one if it is defered free */
+ if (in->deferedFree)
+ return NULL;
+
+ return in;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev, int number,
+ yaffs_ObjectType type)
+{
+ yaffs_Object *theObject=NULL;
+ yaffs_Tnode *tn = NULL;
+
+ if (number < 0)
+ number = yaffs_CreateNewObjectNumber(dev);
+
+ if (type == YAFFS_OBJECT_TYPE_FILE) {
+ tn = yaffs_GetTnode(dev);
+ if (!tn)
+ return NULL;
+ }
+
+ theObject = yaffs_AllocateEmptyObject(dev);
+ if (!theObject){
+ if(tn)
+ yaffs_FreeTnode(dev,tn);
+ return NULL;
+ }
+
+
+ if (theObject) {
+ theObject->fake = 0;
+ theObject->renameAllowed = 1;
+ theObject->unlinkAllowed = 1;
+ theObject->objectId = number;
+ yaffs_HashObject(theObject);
+ theObject->variantType = type;
+#ifdef CONFIG_YAFFS_WINCE
+ yfsd_WinFileTimeNow(theObject->win_atime);
+ theObject->win_ctime[0] = theObject->win_mtime[0] =
+ theObject->win_atime[0];
+ theObject->win_ctime[1] = theObject->win_mtime[1] =
+ theObject->win_atime[1];
+
+#else
+
+ theObject->yst_atime = theObject->yst_mtime =
+ theObject->yst_ctime = Y_CURRENT_TIME;
+#endif
+ switch (type) {
+ case YAFFS_OBJECT_TYPE_FILE:
+ theObject->variant.fileVariant.fileSize = 0;
+ theObject->variant.fileVariant.scannedFileSize = 0;
+ theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; /* max __u32 */
+ theObject->variant.fileVariant.topLevel = 0;
+ theObject->variant.fileVariant.top = tn;
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ YINIT_LIST_HEAD(&theObject->variant.directoryVariant.
+ children);
+ YINIT_LIST_HEAD(&theObject->variant.directoryVariant.
+ dirty);
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ /* No action required */
+ break;
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ /* todo this should not happen */
+ break;
+ }
+ }
+
+ return theObject;
+}
+
+static 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;
+
+}
+
+
+static YCHAR *yaffs_CloneString(const YCHAR *str)
+{
+ YCHAR *newStr = NULL;
+ int len;
+
+ if (!str)
+ str = _Y("");
+
+ len = yaffs_strnlen(str,YAFFS_MAX_ALIAS_LENGTH);
+ newStr = YMALLOC((len + 1) * sizeof(YCHAR));
+ if (newStr){
+ yaffs_strncpy(newStr, str,len);
+ newStr[len] = 0;
+ }
+ return newStr;
+
+}
+
+/*
+ * Mknod (create) a new object.
+ * equivalentObject only has meaning for a hard link;
+ * aliasString only has meaning for a symlink.
+ * rdev only has meaning for devices (a subset of special objects)
+ */
+
+static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type,
+ yaffs_Object *parent,
+ const YCHAR *name,
+ __u32 mode,
+ __u32 uid,
+ __u32 gid,
+ yaffs_Object *equivalentObject,
+ const YCHAR *aliasString, __u32 rdev)
+{
+ yaffs_Object *in;
+ YCHAR *str = NULL;
+
+ 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;
+
+ if (type == YAFFS_OBJECT_TYPE_SYMLINK) {
+ str = yaffs_CloneString(aliasString);
+ if (!str)
+ return NULL;
+ }
+
+ in = yaffs_CreateNewObject(dev, -1, type);
+
+ if (!in){
+ if(str)
+ YFREE(str);
+ return NULL;
+ }
+
+
+
+
+
+ if (in) {
+ in->hdrChunk = 0;
+ in->valid = 1;
+ in->variantType = type;
+
+ in->yst_mode = mode;
+
+#ifdef CONFIG_YAFFS_WINCE
+ yfsd_WinFileTimeNow(in->win_atime);
+ in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
+ in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
+
+#else
+ in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
+
+ in->yst_rdev = rdev;
+ in->yst_uid = uid;
+ in->yst_gid = gid;
+#endif
+ in->nDataChunks = 0;
+
+ yaffs_SetObjectName(in, name);
+ in->dirty = 1;
+
+ yaffs_AddObjectToDirectory(parent, in);
+
+ in->myDev = parent->myDev;
+
+ switch (type) {
+ case YAFFS_OBJECT_TYPE_SYMLINK:
+ in->variant.symLinkVariant.alias = str;
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ in->variant.hardLinkVariant.equivalentObject =
+ equivalentObject;
+ in->variant.hardLinkVariant.equivalentObjectId =
+ equivalentObject->objectId;
+ ylist_add(&in->hardLinks, &equivalentObject->hardLinks);
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ /* do nothing */
+ break;
+ }
+
+ if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) {
+ /* Could not create the object header, fail the creation */
+ yaffs_DeleteObject(in);
+ in = NULL;
+ }
+
+ yaffs_UpdateParent(parent);
+ }
+
+ return in;
+}
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent, const YCHAR *name,
+ __u32 mode, __u32 uid, __u32 gid)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE, parent, name, mode,
+ uid, gid, NULL, NULL, 0);
+}
+
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent, const YCHAR *name,
+ __u32 mode, __u32 uid, __u32 gid)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name,
+ mode, uid, gid, NULL, NULL, 0);
+}
+
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent, const YCHAR *name,
+ __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode,
+ uid, gid, NULL, NULL, rdev);
+}
+
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const YCHAR *name,
+ __u32 mode, __u32 uid, __u32 gid,
+ const YCHAR *alias)
+{
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode,
+ uid, gid, NULL, alias, 0);
+}
+
+/* yaffs_Link returns the object id of the equivalent object.*/
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const YCHAR *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, 0)) {
+ return equivalentObject;
+ } else {
+ return NULL;
+ }
+
+}
+
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir,
+ const YCHAR *newName, int force, int shadows)
+{
+ int unlinkOp;
+ int deleteOp;
+
+ yaffs_Object *existingTarget;
+
+ if (newDir == NULL)
+ newDir = obj->parent; /* use the old directory */
+
+ if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ T(YAFFS_TRACE_ALWAYS,
+ (TSTR
+ ("tragedy: yaffs_ChangeObjectName: newDir is not a directory"
+ TENDSTR)));
+ YBUG();
+ }
+
+ /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */
+ if (obj->myDev->param.isYaffs2)
+ unlinkOp = (newDir == obj->myDev->unlinkedDir);
+ else
+ unlinkOp = (newDir == obj->myDev->unlinkedDir
+ && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
+
+ deleteOp = (newDir == obj->myDev->deletedDir);
+
+ existingTarget = yaffs_FindObjectByName(newDir, newName);
+
+ /* If the object is a file going into the unlinked directory,
+ * then it is OK to just stuff it in since duplicate names are allowed.
+ * else only proceed if the new name does not exist and if we're putting
+ * it into a directory.
+ */
+ if ((unlinkOp ||
+ deleteOp ||
+ force ||
+ (shadows > 0) ||
+ !existingTarget) &&
+ newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) {
+ yaffs_SetObjectName(obj, newName);
+ obj->dirty = 1;
+
+ yaffs_AddObjectToDirectory(newDir, obj);
+
+ if (unlinkOp)
+ obj->unlinked = 1;
+
+ /* If it is a deletion then we mark it as a shrink for gc purposes. */
+ if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows) >= 0)
+ return YAFFS_OK;
+ }
+
+ return YAFFS_FAIL;
+}
+
+int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName,
+ yaffs_Object *newDir, const YCHAR *newName)
+{
+ yaffs_Object *obj = NULL;
+ yaffs_Object *existingTarget = NULL;
+ int force = 0;
+ int result;
+ yaffs_Device *dev;
+
+
+ if (!oldDir || oldDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ YBUG();
+ if (!newDir || newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ YBUG();
+
+ dev = oldDir->myDev;
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+ /* Special case for case insemsitive systems (eg. WinCE).
+ * While look-up is case insensitive, the name isn't.
+ * Therefore we might want to change x.txt to X.txt
+ */
+ if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0)
+ force = 1;
+#endif
+
+ if(yaffs_strnlen(newName,YAFFS_MAX_NAME_LENGTH+1) > YAFFS_MAX_NAME_LENGTH)
+ /* ENAMETOOLONG */
+ return YAFFS_FAIL;
+
+ obj = yaffs_FindObjectByName(oldDir, oldName);
+
+ if (obj && obj->renameAllowed) {
+
+ /* Now do the handling for an existing target, if there is one */
+
+ existingTarget = yaffs_FindObjectByName(newDir, newName);
+ if (existingTarget &&
+ existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
+ !ylist_empty(&existingTarget->variant.directoryVariant.children)) {
+ /* There is a target that is a non-empty directory, so we fail */
+ return YAFFS_FAIL; /* EEXIST or ENOTEMPTY */
+ } else if (existingTarget && existingTarget != obj) {
+ /* Nuke the target first, using shadowing,
+ * but only if it isn't the same object.
+ *
+ * Note we must disable gc otherwise it can mess up the shadowing.
+ *
+ */
+ dev->isDoingGC=1;
+ yaffs_ChangeObjectName(obj, newDir, newName, force,
+ existingTarget->objectId);
+ existingTarget->isShadowed = 1;
+ yaffs_UnlinkObject(existingTarget);
+ dev->isDoingGC=0;
+ }
+
+ result = yaffs_ChangeObjectName(obj, newDir, newName, 1, 0);
+
+ yaffs_UpdateParent(oldDir);
+ if(newDir != oldDir)
+ yaffs_UpdateParent(newDir);
+
+ return result;
+ }
+ return YAFFS_FAIL;
+}
+
+/*------------------------- Block Management and Page Allocation ----------------*/
+
+static int yaffs_InitialiseBlocks(yaffs_Device *dev)
+{
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+
+ dev->blockInfo = NULL;
+ dev->chunkBits = NULL;
+
+ dev->allocationBlock = -1; /* force it to get a new one */
+
+ /* If the first allocation strategy fails, thry the alternate one */
+ dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
+ if (!dev->blockInfo) {
+ dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo));
+ dev->blockInfoAlt = 1;
+ } else
+ dev->blockInfoAlt = 0;
+
+ if (dev->blockInfo) {
+ /* Set up dynamic blockinfo stuff. */
+ dev->chunkBitmapStride = (dev->param.nChunksPerBlock + 7) / 8; /* round up bytes */
+ dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
+ if (!dev->chunkBits) {
+ dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks);
+ dev->chunkBitsAlt = 1;
+ } else
+ dev->chunkBitsAlt = 0;
+ }
+
+ if (dev->blockInfo && dev->chunkBits) {
+ memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo));
+ memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks);
+ return YAFFS_OK;
+ }
+
+ return YAFFS_FAIL;
+}
+
+static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
+{
+ if (dev->blockInfoAlt && dev->blockInfo)
+ YFREE_ALT(dev->blockInfo);
+ else if (dev->blockInfo)
+ YFREE(dev->blockInfo);
+
+ dev->blockInfoAlt = 0;
+
+ dev->blockInfo = NULL;
+
+ if (dev->chunkBitsAlt && dev->chunkBits)
+ YFREE_ALT(dev->chunkBits);
+ else if (dev->chunkBits)
+ YFREE(dev->chunkBits);
+ dev->chunkBitsAlt = 0;
+ dev->chunkBits = NULL;
+}
+
+static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev,
+ yaffs_BlockInfo *bi)
+{
+
+ if (!dev->param.isYaffs2)
+ return 1; /* disqualification only applies to yaffs2. */
+
+ if (!bi->hasShrinkHeader)
+ return 1; /* can gc */
+
+ yaffs_FindOldestDirtySequence(dev);
+
+ /* Can't do gc of this block if there are any blocks older than this one that have
+ * discarded pages.
+ */
+ return (bi->sequenceNumber <= dev->oldestDirtySequence);
+}
+
+/*
+ * yaffs_FindRefreshBlock()
+ * periodically finds the oldest full block by sequence number for refreshing.
+ * Only for yaffs2.
+ */
+static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
+{
+ __u32 b ;
+
+ __u32 oldest = 0;
+ __u32 oldestSequence = 0;
+
+ yaffs_BlockInfo *bi;
+
+ /*
+ * If refresh period < 10 then refreshing is disabled.
+ */
+ if(dev->param.refreshPeriod < 10 ||
+ !dev->param.isYaffs2)
+ return oldest;
+
+ /*
+ * Fix broken values.
+ */
+ if(dev->refreshSkip > dev->param.refreshPeriod)
+ dev->refreshSkip = dev->param.refreshPeriod;
+
+ if(dev->refreshSkip > 0){
+ dev->refreshSkip--;
+ return oldest;
+ }
+
+ /*
+ * Refresh skip is now zero.
+ * We'll do a refresh this time around....
+ * Update the refresh skip and find the oldest block.
+ */
+ dev->refreshSkip = dev->param.refreshPeriod;
+ dev->refreshCount++;
+ bi = dev->blockInfo;
+ for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){
+
+ if (bi->blockState == YAFFS_BLOCK_STATE_FULL){
+
+ if(oldest < 1 ||
+ bi->sequenceNumber < oldestSequence){
+ oldest = b;
+ oldestSequence = bi->sequenceNumber;
+ }
+ }
+ bi++;
+ }
+
+ if (oldest > 0) {
+ T(YAFFS_TRACE_GC,
+ (TSTR("GC refresh count %d selected block %d with sequenceNumber %d" TENDSTR),
+ dev->refreshCount, oldest, oldestSequence));
+ }
+
+ return oldest;
+}
+
+/*
+ * FindDiretiestBlock is used to select the dirtiest block (or close enough)
+ * for garbage collection.
+ */
+
+static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
+ int aggressive)
+{
+ int b = dev->currentDirtyChecker;
+
+ int i;
+ int iterations;
+ int dirtiest = -1;
+ int pagesInUse = 0;
+ int prioritised = 0;
+ yaffs_BlockInfo *bi;
+ int pendingPrioritisedExist = 0;
+
+ /* First let's see if we need to grab a prioritised block */
+ if (dev->hasPendingPrioritisedGCs) {
+ bi = dev->blockInfo;
+ for (i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++) {
+
+ if (bi->gcPrioritise) {
+ pendingPrioritisedExist = 1;
+ if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+ yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
+ pagesInUse = (bi->pagesInUse - bi->softDeletions);
+ dirtiest = i;
+ prioritised = 1;
+ aggressive = 1; /* Fool the non-aggressive skip logiv below */
+ }
+ }
+ bi++;
+ }
+
+ if (!pendingPrioritisedExist) /* None found, so we can clear this */
+ dev->hasPendingPrioritisedGCs = 0;
+ }
+
+ /* If we're doing aggressive GC then we are happy to take a less-dirty block, and
+ * search harder.
+ * else (we're doing a leasurely gc), then we only bother to do this if the
+ * block has only a few pages in use.
+ */
+
+ dev->nonAggressiveSkip--;
+
+ if (!aggressive && (dev->nonAggressiveSkip > 0))
+ return -1;
+
+ if (!prioritised)
+ pagesInUse =
+ (aggressive) ? dev->param.nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+
+ if (aggressive)
+ iterations =
+ dev->internalEndBlock - dev->internalStartBlock + 1;
+ else {
+ iterations =
+ dev->internalEndBlock - dev->internalStartBlock + 1;
+ iterations = iterations / 16;
+ if (iterations > 200)
+ iterations = 200;
+ }
+
+ for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) {
+ b++;
+ if (b < dev->internalStartBlock || b > dev->internalEndBlock)
+ b = dev->internalStartBlock;
+
+ if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("**>> Block %d is not valid" TENDSTR), b));
+ YBUG();
+ }
+
+ bi = yaffs_GetBlockInfo(dev, b);
+
+ if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+ (bi->pagesInUse - bi->softDeletions) < pagesInUse &&
+ yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
+ dirtiest = b;
+ pagesInUse = (bi->pagesInUse - bi->softDeletions);
+ }
+ }
+
+ dev->currentDirtyChecker = b;
+
+ if (dirtiest > 0) {
+ T(YAFFS_TRACE_GC,
+ (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest,
+ dev->param.nChunksPerBlock - pagesInUse, prioritised));
+ }
+
+ if (dirtiest > 0)
+ dev->nonAggressiveSkip = 4;
+
+ return dirtiest;
+}
+
+static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo)
+{
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo);
+
+ int erasedOk = 0;
+
+ /* If the block is still healthy erase it and mark as clean.
+ * If the block has had a data failure, then retire it.
+ */
+
+ T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
+ (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR),
+ blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : ""));
+
+ yaffs_ClearOldestDirtySequence(dev,bi);
+
+ bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
+
+ if (!bi->needsRetiring) {
+ yaffs_InvalidateCheckpoint(dev);
+ erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
+ if (!erasedOk) {
+ dev->nErasureFailures++;
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ (TSTR("**>> Erasure failed %d" TENDSTR), blockNo));
+ }
+ }
+
+ if (erasedOk &&
+ ((yaffs_traceMask & YAFFS_TRACE_ERASE) || !yaffs_SkipVerification(dev))) {
+ int i;
+ for (i = 0; i < dev->param.nChunksPerBlock; i++) {
+ if (!yaffs_CheckChunkErased
+ (dev, blockNo * dev->param.nChunksPerBlock + i)) {
+ T(YAFFS_TRACE_ERROR,
+ (TSTR
+ (">>Block %d erasure supposedly OK, but chunk %d not erased"
+ TENDSTR), blockNo, i));
+ }
+ }
+ }
+
+ if (erasedOk) {
+ /* Clean it up... */
+ bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+ bi->sequenceNumber = 0;
+ dev->nErasedBlocks++;
+ bi->pagesInUse = 0;
+ bi->softDeletions = 0;
+ bi->hasShrinkHeader = 0;
+ bi->skipErasedCheck = 1; /* This is clean, so no need to check */
+ bi->gcPrioritise = 0;
+ yaffs_ClearChunkBits(dev, blockNo);
+
+ T(YAFFS_TRACE_ERASE,
+ (TSTR("Erased block %d" TENDSTR), blockNo));
+ } else {
+ dev->nFreeChunks -= dev->param.nChunksPerBlock; /* We lost a block of free space */
+
+ yaffs_RetireBlock(dev, blockNo);
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ (TSTR("**>> Block %d retired" TENDSTR), blockNo));
+ }
+}
+
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
+{
+ int i;
+
+ yaffs_BlockInfo *bi;
+
+ if (dev->nErasedBlocks < 1) {
+ /* Hoosterman we've got a problem.
+ * Can't get space to gc
+ */
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("yaffs tragedy: no more erased blocks" TENDSTR)));
+
+ return -1;
+ }
+
+ /* Find an empty block. */
+
+ for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
+ dev->allocationBlockFinder++;
+ if (dev->allocationBlockFinder < dev->internalStartBlock
+ || dev->allocationBlockFinder > dev->internalEndBlock) {
+ dev->allocationBlockFinder = dev->internalStartBlock;
+ }
+
+ bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder);
+
+ if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) {
+ bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->sequenceNumber++;
+ bi->sequenceNumber = dev->sequenceNumber;
+ dev->nErasedBlocks--;
+ T(YAFFS_TRACE_ALLOCATE,
+ (TSTR("Allocated block %d, seq %d, %d left" TENDSTR),
+ dev->allocationBlockFinder, dev->sequenceNumber,
+ dev->nErasedBlocks));
+ return dev->allocationBlockFinder;
+ }
+ }
+
+ T(YAFFS_TRACE_ALWAYS,
+ (TSTR
+ ("yaffs tragedy: no more erased blocks, but there should have been %d"
+ TENDSTR), dev->nErasedBlocks));
+
+ return -1;
+}
+
+
+
+static int yaffs_CalcCheckpointBlocksRequired(yaffs_Device *dev)
+{
+ if (!dev->nCheckpointBlocksRequired &&
+ dev->param.isYaffs2) {
+ /* Not a valid value so recalculate */
+ int nBytes = 0;
+ int nBlocks;
+ int devBlocks = (dev->param.endBlock - dev->param.startBlock + 1);
+ int tnodeSize = yaffs_CalcTnodeSize(dev);
+
+ nBytes += sizeof(yaffs_CheckpointValidity);
+ nBytes += sizeof(yaffs_CheckpointDevice);
+ nBytes += devBlocks * sizeof(yaffs_BlockInfo);
+ nBytes += devBlocks * dev->chunkBitmapStride;
+ nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjectsCreated - dev->nFreeObjects);
+ nBytes += (tnodeSize + sizeof(__u32)) * (dev->nTnodesCreated - dev->nFreeTnodes);
+ nBytes += sizeof(yaffs_CheckpointValidity);
+ nBytes += sizeof(__u32); /* checksum*/
+
+ /* Round up and add 2 blocks to allow for some bad blocks, so add 3 */
+
+ nBlocks = (nBytes/(dev->nDataBytesPerChunk * dev->param.nChunksPerBlock)) + 3;
+
+ dev->nCheckpointBlocksRequired = nBlocks;
+ }
+
+ return dev->nCheckpointBlocksRequired;
+}
+
+/*
+ * Check if there's space to allocate...
+ * Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()?
+ */
+static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev, int nChunks)
+{
+ int reservedChunks;
+ int reservedBlocks = dev->param.nReservedBlocks;
+ int checkpointBlocks;
+
+ if (dev->param.isYaffs2) {
+ checkpointBlocks = yaffs_CalcCheckpointBlocksRequired(dev) -
+ dev->blocksInCheckpoint;
+ if (checkpointBlocks < 0)
+ checkpointBlocks = 0;
+ } else {
+ checkpointBlocks = 0;
+ }
+
+ reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->param.nChunksPerBlock);
+
+ return (dev->nFreeChunks > (reservedChunks + nChunks));
+}
+
+static int yaffs_AllocateChunk(yaffs_Device *dev, int useReserve,
+ yaffs_BlockInfo **blockUsedPtr)
+{
+ int retVal;
+ yaffs_BlockInfo *bi;
+
+ if (dev->allocationBlock < 0) {
+ /* Get next block to allocate off */
+ dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
+ dev->allocationPage = 0;
+ }
+
+ if (!useReserve && !yaffs_CheckSpaceForAllocation(dev, 1)) {
+ /* Not enough space to allocate unless we're allowed to use the reserve. */
+ return -1;
+ }
+
+ if (dev->nErasedBlocks < dev->param.nReservedBlocks
+ && dev->allocationPage == 0) {
+ T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR)));
+ }
+
+ /* Next page please.... */
+ if (dev->allocationBlock >= 0) {
+ bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
+
+ retVal = (dev->allocationBlock * dev->param.nChunksPerBlock) +
+ dev->allocationPage;
+ bi->pagesInUse++;
+ yaffs_SetChunkBit(dev, dev->allocationBlock,
+ dev->allocationPage);
+
+ dev->allocationPage++;
+
+ dev->nFreeChunks--;
+
+ /* If the block is full set the state to full */
+ if (dev->allocationPage >= dev->param.nChunksPerBlock) {
+ bi->blockState = YAFFS_BLOCK_STATE_FULL;
+ dev->allocationBlock = -1;
+ }
+
+ if (blockUsedPtr)
+ *blockUsedPtr = bi;
+
+ return retVal;
+ }
+
+ T(YAFFS_TRACE_ERROR,
+ (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
+
+ return -1;
+}
+
+static int yaffs_GetErasedChunks(yaffs_Device *dev)
+{
+ int n;
+
+ n = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+
+ if (dev->allocationBlock > 0)
+ n += (dev->param.nChunksPerBlock - dev->allocationPage);
+
+ return n;
+
+}
+
+/*
+ * yaffs_SkipRestOfBlock() skips over the rest of the allocation block
+ * if we don't want to write to it.
+ */
+static void yaffs_SkipRestOfBlock(yaffs_Device *dev)
+{
+ if(dev->allocationBlock > 0){
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->allocationBlock);
+ if(bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING){
+ bi->blockState = YAFFS_BLOCK_STATE_FULL;
+ dev->allocationBlock = -1;
+ }
+ }
+}
+
+
+static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
+ int wholeBlock)
+{
+ int oldChunk;
+ int newChunk;
+ int markNAND;
+ int retVal = YAFFS_OK;
+ int cleanups = 0;
+ int i;
+ int isCheckpointBlock;
+ int matchingChunk;
+ int maxCopies;
+
+ int chunksBefore = yaffs_GetErasedChunks(dev);
+ int chunksAfter;
+
+ yaffs_ExtendedTags tags;
+
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block);
+
+ yaffs_Object *object;
+
+ isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT);
+
+
+ T(YAFFS_TRACE_TRACING,
+ (TSTR("Collecting block %d, in use %d, shrink %d, wholeBlock %d" TENDSTR),
+ block,
+ bi->pagesInUse,
+ bi->hasShrinkHeader,
+ wholeBlock));
+
+ /*yaffs_VerifyFreeChunks(dev); */
+
+ if(bi->blockState == YAFFS_BLOCK_STATE_FULL)
+ bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
+
+ bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */
+
+ /* Take off the number of soft deleted entries because