- return YAFFS_FAIL;
-
-}
-
-static void yaffs_DeinitialiseBlocks(yaffs_Device * dev)
-{
- if(dev->blockInfoAlt)
- YFREE_ALT(dev->blockInfo);
- else
- YFREE(dev->blockInfo);
- dev->blockInfoAlt = 0;
-
- dev->blockInfo = NULL;
-
- if(dev->chunkBitsAlt)
- YFREE_ALT(dev->chunkBits);
- else
- YFREE(dev->chunkBits);
- dev->chunkBitsAlt = 0;
- dev->chunkBits = NULL;
-}
-
-static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev,
- yaffs_BlockInfo * bi)
-{
- int i;
- __u32 seq;
- yaffs_BlockInfo *b;
-
- if (!dev->isYaffs2)
- return 1; /* disqualification only applies to yaffs2. */
-
- if (!bi->hasShrinkHeader)
- return 1; /* can gc */
-
- /* Find the oldest dirty sequence number if we don't know it and save it
- * so we don't have to keep recomputing it.
- */
- if (!dev->oldestDirtySequence) {
- seq = dev->sequenceNumber;
-
- for (i = dev->internalStartBlock; i <= dev->internalEndBlock;
- i++) {
- b = yaffs_GetBlockInfo(dev, i);
- if (b->blockState == YAFFS_BLOCK_STATE_FULL &&
- (b->pagesInUse - b->softDeletions) <
- dev->nChunksPerBlock && b->sequenceNumber < seq) {
- seq = b->sequenceNumber;
- }
- }
- dev->oldestDirtySequence = seq;
- }
-
- /* 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);
-
-}
-
-/* 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;
- int prioritised=0;
- yaffs_BlockInfo *bi;
- static int nonAggressiveSkip = 0;
- int pendingPrioritisedExist = 0;
-
- /* First let's see if we need to grab a prioritised block */
- if(dev->hasPendingPrioritisedGCs){
- for(i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++){
-
- bi = yaffs_GetBlockInfo(dev, i);
- if(bi->gcPrioritise)
- pendingPrioritisedExist = 1;
- if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
- bi->gcPrioritise &&
- yaffs_BlockNotDisqualifiedFromGC(dev, bi)){
- pagesInUse = (bi->pagesInUse - bi->softDeletions);
- dirtiest = i;
- prioritised = 1;
- aggressive = 1; /* Fool the non-aggressive skip logiv below */
- }
- }
-
- 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.
- */
-
- nonAggressiveSkip--;
-
- if (!aggressive && (nonAggressiveSkip > 0)) {
- return -1;
- }
-
- if(!prioritised)
- pagesInUse =
- (aggressive) ? dev->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 0
- if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) {
- dirtiest = b;
- pagesInUse = 0;
- }
- else
-#endif
-
- 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->nChunksPerBlock - pagesInUse,prioritised));
- }
-
- dev->oldestDirtySequence = 0;
-
- if (dirtiest > 0) {
- 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.
- */
- 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)) {
- int i;
- for (i = 0; i < dev->nChunksPerBlock; i++) {
- if (!yaffs_CheckChunkErased
- (dev, blockNo * dev->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;
- 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->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 eraased 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 eraased blocks, but there should have been %d"
- TENDSTR), dev->nErasedBlocks));
-
- return -1;
-}
-
-
-// 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 reservedChunks;
- int reservedBlocks = dev->nReservedBlocks;
- int checkpointBlocks;
-
- checkpointBlocks = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint;
- if(checkpointBlocks < 0)
- checkpointBlocks = 0;
-
- reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->nChunksPerBlock);
-
- return (dev->nFreeChunks > reservedChunks);
-}
-
-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)) {
- /* Not enough space to allocate unless we're allowed to use the reserve. */
- return -1;
- }
-
- if (dev->nErasedBlocks < dev->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->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->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->nChunksPerBlock;
-
- if (dev->allocationBlock > 0) {
- n += (dev->nChunksPerBlock - dev->allocationPage);
- }
-
- return n;
-
-}
-
-static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block)
-{
- int oldChunk;
- int newChunk;
- int chunkInBlock;
- int markNAND;
- int retVal = YAFFS_OK;
- int cleanups = 0;
- int i;
- int isCheckpointBlock;
-
- 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);
-
- bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
-
- T(YAFFS_TRACE_TRACING,
- (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block,
- bi->pagesInUse, bi->hasShrinkHeader));
-
- /*yaffs_VerifyFreeChunks(dev); */
-
- bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */
-
- /* Take off the number of soft deleted entries because
- * they're going to get really deleted during GC.
- */
- dev->nFreeChunks -= bi->softDeletions;
-
- dev->isDoingGC = 1;
-
- if (isCheckpointBlock ||
- !yaffs_StillSomeChunkBits(dev, block)) {
- T(YAFFS_TRACE_TRACING,
- (TSTR
- ("Collecting block %d that has no chunks in use" TENDSTR),
- block));
- yaffs_BlockBecameDirty(dev, block);
- } else {
-
- __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__);
-
- for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock;
- chunkInBlock < dev->nChunksPerBlock
- && yaffs_StillSomeChunkBits(dev, block);
- chunkInBlock++, oldChunk++) {
- if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) {
-
- /* This page is in use and might need to be copied off */
-
- markNAND = 1;
-
- yaffs_InitialiseTags(&tags);
-
- yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk,
- buffer, &tags);
-
- object =
- yaffs_FindObjectByNumber(dev,
- tags.objectId);
-
- T(YAFFS_TRACE_GC_DETAIL,
- (TSTR
- ("Collecting page %d, %d %d %d " TENDSTR),
- chunkInBlock, tags.objectId, tags.chunkId,
- tags.byteCount));
-
- if (!object) {
- T(YAFFS_TRACE_ERROR,
- (TSTR
- ("page %d in gc has no object "
- TENDSTR), oldChunk));
- }
-
- if (object && object->deleted
- && tags.chunkId != 0) {
- /* Data chunk in a deleted file, throw it away
- * It's a soft deleted data chunk,
- * No need to copy this, just forget about it and
- * fix up the object.
- */
-
- object->nDataChunks--;
-
- if (object->nDataChunks <= 0) {
- /* remeber to clean up the object */
- dev->gcCleanupList[cleanups] =
- tags.objectId;
- cleanups++;
- }
- markNAND = 0;
- } else if (0
- /* Todo object && object->deleted && object->nDataChunks == 0 */
- ) {
- /* Deleted object header with no data chunks.
- * Can be discarded and the file deleted.
- */
- object->chunkId = 0;
- yaffs_FreeTnode(object->myDev,
- object->variant.
- fileVariant.top);
- object->variant.fileVariant.top = NULL;
- yaffs_DoGenericObjectDeletion(object);
-
- } else if (object) {
- /* It's either a data chunk in a live file or
- * an ObjectHeader, so we're interested in it.
- * NB Need to keep the ObjectHeaders of deleted files
- * until the whole file has been deleted off
- */
- tags.serialNumber++;
-
- dev->nGCCopies++;
-
- if (tags.chunkId == 0) {
- /* It is an object Id,
- * We need to nuke the shrinkheader flags first
- * We no longer want the shrinkHeader flag since its work is done
- * and if it is left in place it will mess up scanning.
- * Also, clear out any shadowing stuff
- */
-
- yaffs_ObjectHeader *oh;
- oh = (yaffs_ObjectHeader *)buffer;
- oh->isShrink = 0;
- oh->shadowsObject = -1;
- tags.extraShadows = 0;
- tags.extraIsShrinkHeader = 0;
- }
-
- newChunk =
- yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1);
-
- if (newChunk < 0) {
- retVal = YAFFS_FAIL;
- } else {
-
- /* Ok, now fix up the Tnodes etc. */
-
- if (tags.chunkId == 0) {
- /* It's a header */
- object->chunkId = newChunk;
- object->serial = tags.serialNumber;
- } else {
- /* It's a data chunk */
- yaffs_PutChunkIntoFile
- (object,
- tags.chunkId,
- newChunk, 0);
- }
- }
- }
-
- yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__);
-
- }
- }
-
- yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
-
-
- /* Do any required cleanups */
- for (i = 0; i < cleanups; i++) {
- /* Time to delete the file too */
- object =
- yaffs_FindObjectByNumber(dev,
- dev->gcCleanupList[i]);
- if (object) {
- yaffs_FreeTnode(dev,
- object->variant.fileVariant.
- top);
- object->variant.fileVariant.top = NULL;
- T(YAFFS_TRACE_GC,
- (TSTR
- ("yaffs: About to finally delete object %d"
- TENDSTR), object->objectId));
- yaffs_DoGenericObjectDeletion(object);
- object->myDev->nDeletedFiles--;
- }
-
- }
-
- }
-
- if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) {
- T(YAFFS_TRACE_GC,
- (TSTR
- ("gc did not increase free chunks before %d after %d"
- TENDSTR), chunksBefore, chunksAfter));
- }
-
- dev->isDoingGC = 0;
-
- return YAFFS_OK;
-}
-
-/* New garbage collector
- * If we're very low on erased blocks then we do aggressive garbage collection
- * otherwise we do "leasurely" garbage collection.
- * Aggressive gc looks further (whole array) and will accept less dirty blocks.
- * Passive gc only inspects smaller areas and will only accept more dirty blocks.
- *
- * The idea is to help clear out space in a more spread-out manner.
- * Dunno if it really does anything useful.
- */
-static int yaffs_CheckGarbageCollection(yaffs_Device * dev)
-{
- int block;
- int aggressive;
- int gcOk = YAFFS_OK;
- int maxTries = 0;
-
- int checkpointBlockAdjust;
-
- if (dev->isDoingGC) {
- /* Bail out so we don't get recursive gc */
- return YAFFS_OK;
- }
-
- /* This loop should pass the first time.
- * We'll only see looping here if the erase of the collected block fails.
- */
-
- do {
- maxTries++;
-
- checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint);
- if(checkpointBlockAdjust < 0)
- checkpointBlockAdjust = 0;
-
- if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust)) {
- /* We need a block soon...*/
- aggressive = 1;
- } else {
- /* We're in no hurry */
- aggressive = 0;
- }
-
- block = yaffs_FindBlockForGarbageCollection(dev, aggressive);
-
- if (block > 0) {
- dev->garbageCollections++;
- if (!aggressive) {
- dev->passiveGarbageCollections++;
- }
-
- T(YAFFS_TRACE_GC,
- (TSTR
- ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),
- dev->nErasedBlocks, aggressive));
-
- gcOk = yaffs_GarbageCollectBlock(dev, block);
- }
-
- if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) {
- T(YAFFS_TRACE_GC,
- (TSTR
- ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d"
- TENDSTR), dev->nErasedBlocks, maxTries, block));
- }
- } while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0)
- && (maxTries < 2));
-
- return aggressive ? gcOk : YAFFS_OK;
-}
-
-/*------------------------- TAGS --------------------------------*/
-
-static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
- int chunkInObject)
-{
- return (tags->chunkId == chunkInObject &&
- tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0;
-
-}
-
-
-/*-------------------- Data file manipulation -----------------*/
-
-static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode,
- yaffs_ExtendedTags * tags)
-{
- /*Get the Tnode, then get the level 0 offset chunk offset */
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_ExtendedTags localTags;
- int retVal = -1;
-
- 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 = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
- retVal =
- yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
- chunkInInode);
- }
- return retVal;
-}
-
-static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode,
- yaffs_ExtendedTags * tags)
-{
- /* Get the Tnode, then get the level 0 offset chunk offset */
- yaffs_Tnode *tn;
- int theChunk = -1;
- yaffs_ExtendedTags localTags;
-
- yaffs_Device *dev = in->myDev;
- int retVal = -1;
-
- if (!tags) {
- /* Passed a NULL, so use our own tags space */
- tags = &localTags;
- }
-
- tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode);
-
- if (tn) {
-
- theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
- retVal =
- yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId,
- chunkInInode);
-
- /* Delete the entry in the filestructure (if found) */
- if (retVal != -1) {
- yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0);
- }
- } else {
- /*T(("No level 0 found for %d\n", chunkInInode)); */
- }
-
- if (retVal == -1) {
- /* T(("Could not find %d to delete\n",chunkInInode)); */
- }
- return retVal;
-}
-
-#ifdef 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;
- int chunkDeleted;
-
- 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 + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk;
-
- for (chunk = 1; chunk <= nChunks; chunk++) {
- tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant,
- chunk);
-
- if (tn) {
-
- theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk);
-
- if (yaffs_CheckChunkBits
- (dev, theChunk / dev->nChunksPerBlock,
- theChunk % dev->nChunksPerBlock)) {
-
- yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk,
- tags,
- &chunkDeleted);
- if (yaffs_TagsMatch
- (tags, in->objectId, chunk, chunkDeleted)) {
- /* found it; */
-
- }
- } else {
-
- 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)
-{
- /* NB inScan is zero unless scanning.
- * For forward scanning, inScan is > 0;
- * for backward scanning inScan is < 0
- */
-
- yaffs_Tnode *tn;
- yaffs_Device *dev = in->myDev;
- int existingChunk;
- yaffs_ExtendedTags existingTags;
- yaffs_ExtendedTags newTags;
- unsigned existingSerial, newSerial;
-
- if (in->variantType != YAFFS_OBJECT_TYPE_FILE) {
- /* Just ignore an attempt at putting a chunk into a non-file during scanning
- * If it is not during Scanning then something went wrong!
- */
- if (!inScan) {
- T(YAFFS_TRACE_ERROR,
- (TSTR
- ("yaffs tragedy:attempt to put data chunk into a non-file"
- TENDSTR)));
- YBUG();
- }
-
- yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
- return YAFFS_OK;
- }
-
- tn = yaffs_AddOrFindLevel0Tnode(dev,
- &in->variant.fileVariant,
- chunkInInode,
- NULL);
- if (!tn) {
- return YAFFS_FAIL;
- }
-
- existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
-
- if (inScan != 0) {
- /* If we're scanning then we need to test for duplicates
- * NB This does not need to be efficient since it should only ever
- * happen when the power fails during a write, then only one
- * chunk should ever be affected.
- *
- * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
- * Update: For backward scanning we don't need to re-read tags so this is quite cheap.
- */
-
- if (existingChunk != 0) {
- /* NB Right now existing chunk will not be real chunkId if the device >= 32MB
- * thus we have to do a FindChunkInFile to get the real chunk id.
- *
- * We have a duplicate now we need to decide which one to use:
- *
- * Backwards scanning YAFFS2: The old one is what we use, dump the new one.
- * Forward scanning YAFFS2: The new one is what we use, dump the old one.
- * YAFFS1: Get both sets of tags and compare serial numbers.
- */
-
- if (inScan > 0) {
- /* Only do this for forward scanning */
- yaffs_ReadChunkWithTagsFromNAND(dev,
- chunkInNAND,
- NULL, &newTags);
-
- /* Do a proper find */
- existingChunk =
- yaffs_FindChunkInFile(in, chunkInInode,
- &existingTags);
- }
-
- if (existingChunk <= 0) {
- /*Hoosterman - how did this happen? */
-
- T(YAFFS_TRACE_ERROR,
- (TSTR
- ("yaffs tragedy: existing chunk < 0 in scan"
- TENDSTR)));
-
- }
-
- /* NB The deleted flags should be false, otherwise the chunks will
- * not be loaded during a scan
- */
-
- newSerial = newTags.serialNumber;
- existingSerial = existingTags.serialNumber;
-
- if ((inScan > 0) &&
- (in->myDev->isYaffs2 ||
- existingChunk <= 0 ||
- ((existingSerial + 1) & 3) == newSerial)) {
- /* Forward scanning.
- * Use new
- * Delete the old one and drop through to update the tnode
- */
- yaffs_DeleteChunk(dev, existingChunk, 1,
- __LINE__);
- } else {
- /* Backward scanning or we want to use the existing one
- * Use existing.
- * Delete the new one and return early so that the tnode isn't changed
- */
- yaffs_DeleteChunk(dev, chunkInNAND, 1,
- __LINE__);
- return YAFFS_OK;
- }
- }
-
- }
-
- if (existingChunk == 0) {
- in->nDataChunks++;
- }
-
- yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND);
-
- return YAFFS_OK;
-}
-
-static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode,
- __u8 * buffer)
-{
- int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL);
-
- if (chunkInNAND >= 0) {
- return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND,
- buffer,NULL);
- } else {
- T(YAFFS_TRACE_NANDACCESS,
- (TSTR("Chunk %d not found zero instead" TENDSTR),
- chunkInNAND));
- /* get sane (zero) data if you read a hole */
- memset(buffer, 0, in->myDev->nDataBytesPerChunk);
- return 0;
- }
-
-}
-
-void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn)
-{
- int block;
- int page;
- yaffs_ExtendedTags tags;
- yaffs_BlockInfo *bi;
-
- if (chunkId <= 0)
- return;
-
- dev->nDeletions++;
- block = chunkId / dev->nChunksPerBlock;
- page = chunkId % dev->nChunksPerBlock;
-
- bi = yaffs_GetBlockInfo(dev, block);
-
- T(YAFFS_TRACE_DELETION,
- (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
-
- if (markNAND &&
- bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) {
-
- yaffs_InitialiseTags(&tags);
-
- tags.chunkDeleted = 1;
-
- yaffs_WriteChunkWithTagsToNAND(dev, chunkId, NULL, &tags);
- yaffs_HandleUpdateChunk(dev, chunkId, &tags);
- } else {
- dev->nUnmarkedDeletions++;
- }
-
- /* Pull out of the management area.
- * If the whole block became dirty, this will kick off an erasure.
- */
- if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
- bi->blockState == YAFFS_BLOCK_STATE_FULL ||
- bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
- bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) {
- dev->nFreeChunks++;
-
- yaffs_ClearChunkBit(dev, block, page);
-
- bi->pagesInUse--;
-
- if (bi->pagesInUse == 0 &&
- !bi->hasShrinkHeader &&
- bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING &&
- bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
- yaffs_BlockBecameDirty(dev, block);
- }
-
- } else {
- /* T(("Bad news deleting chunk %d\n",chunkId)); */
- }
-
-}
-
-static int yaffs_WriteChunkDataToObject(yaffs_Object * in, int chunkInInode,
- const __u8 * buffer, int nBytes,
- int useReserve)
-{
- /* Find old chunk Need to do this to get serial number
- * Write new one and patch into tree.
- * Invalidate old tags.
- */
-
- int prevChunkId;
- yaffs_ExtendedTags prevTags;
-
- int newChunkId;
- yaffs_ExtendedTags 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 */
- yaffs_InitialiseTags(&newTags);
-
- newTags.chunkId = chunkInInode;
- newTags.objectId = in->objectId;
- newTags.serialNumber =
- (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
- newTags.byteCount = nBytes;
-
- newChunkId =
- yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
- useReserve);
-
- if (newChunkId >= 0) {
- yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0);
-
- if (prevChunkId >= 0) {
- yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__);
-
- }
-
- yaffs_CheckFileSanity(in);
- }
- return newChunkId;
-
-}
-
-/* UpdateObjectHeader updates the header on NAND for an object.
- * If name is not NULL, then that new name is used.
- */
-int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force,
- int isShrink, int shadows)
-{
-
- yaffs_BlockInfo *bi;
-
- yaffs_Device *dev = in->myDev;
-
- int prevChunkId;
- int retVal = 0;
- int result = 0;
-
- int newChunkId;
- yaffs_ExtendedTags newTags;
-
- __u8 *buffer = NULL;
- YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1];
-
- yaffs_ObjectHeader *oh = NULL;
-
- if (!in->fake || force) {
-
- yaffs_CheckGarbageCollection(dev);
-
- buffer = yaffs_GetTempBuffer(in->myDev, __LINE__);
- oh = (yaffs_ObjectHeader *) buffer;
-
- prevChunkId = in->chunkId;
-
- if (prevChunkId >= 0) {
- result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId,
- buffer, NULL);
- memcpy(oldName, oh->name, sizeof(oh->name));
- }
-
- memset(buffer, 0xFF, dev->nDataBytesPerChunk);
-
- oh->type = in->variantType;
- oh->yst_mode = in->yst_mode;
- oh->shadowsObject = shadows;
-
-#ifdef CONFIG_YAFFS_WINCE
- oh->win_atime[0] = in->win_atime[0];
- oh->win_ctime[0] = in->win_ctime[0];
- oh->win_mtime[0] = in->win_mtime[0];
- oh->win_atime[1] = in->win_atime[1];
- oh->win_ctime[1] = in->win_ctime[1];
- oh->win_mtime[1] = in->win_mtime[1];
-#else
- oh->yst_uid = in->yst_uid;
- oh->yst_gid = in->yst_gid;
- oh->yst_atime = in->yst_atime;
- oh->yst_mtime = in->yst_mtime;
- oh->yst_ctime = in->yst_ctime;
- oh->yst_rdev = in->yst_rdev;
-#endif
- if (in->parent) {
- oh->parentObjectId = in->parent->objectId;
- } else {
- oh->parentObjectId = 0;
- }
-
- if (name && *name) {
- memset(oh->name, 0, sizeof(oh->name));
- yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH);
- } else if (prevChunkId) {
- memcpy(oh->name, oldName, sizeof(oh->name));
- } else {
- memset(oh->name, 0, sizeof(oh->name));
- }
-
- oh->isShrink = isShrink;
-
- switch (in->variantType) {
- case YAFFS_OBJECT_TYPE_UNKNOWN:
- /* Should not happen */
- break;
- case YAFFS_OBJECT_TYPE_FILE:
- oh->fileSize =
- (oh->parentObjectId == YAFFS_OBJECTID_DELETED
- || oh->parentObjectId ==
- YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant.
- fileVariant.fileSize;
- break;
- case YAFFS_OBJECT_TYPE_HARDLINK:
- oh->equivalentObjectId =
- in->variant.hardLinkVariant.equivalentObjectId;
- break;
- case YAFFS_OBJECT_TYPE_SPECIAL:
- /* Do nothing */
- break;
- case YAFFS_OBJECT_TYPE_DIRECTORY:
- /* Do nothing */
- break;
- case YAFFS_OBJECT_TYPE_SYMLINK:
- yaffs_strncpy(oh->alias,
- in->variant.symLinkVariant.alias,
- YAFFS_MAX_ALIAS_LENGTH);
- oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
- break;
- }
-
- /* Tags */
- yaffs_InitialiseTags(&newTags);
- in->serial++;
- newTags.chunkId = 0;
- newTags.objectId = in->objectId;
- newTags.serialNumber = in->serial;
-
- /* Add extra info for file header */
-
- newTags.extraHeaderInfoAvailable = 1;
- newTags.extraParentObjectId = oh->parentObjectId;
- newTags.extraFileLength = oh->fileSize;
- newTags.extraIsShrinkHeader = oh->isShrink;
- newTags.extraEquivalentObjectId = oh->equivalentObjectId;
- newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0;
- newTags.extraObjectType = in->variantType;
-
- /* Create new chunk in NAND */
- newChunkId =
- yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
- (prevChunkId >= 0) ? 1 : 0);
-
- if (newChunkId >= 0) {
-
- in->chunkId = newChunkId;
-
- if (prevChunkId >= 0) {
- yaffs_DeleteChunk(dev, prevChunkId, 1,
- __LINE__);
- }
-
- if(!yaffs_ObjectHasCachedWriteData(in))
- in->dirty = 0;