- 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
- */
-
- if(inScan > 0) {
- 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;
-
-
- if(!yaffs_CheckChunkBit(dev,block,page))
- T(YAFFS_TRACE_VERIFY,
- (TSTR("Deleting invalid chunk %d"TENDSTR),
- chunkId));
-
- 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;
-
- if(nBytes < 1 || nBytes > dev->totalBytesPerChunk){
- T(YAFFS_TRACE_ERROR,
- (TSTR("Writing %d bytes to chunk!!!!!!!!!" TENDSTR), nBytes));
- YBUG();
- }
-
-
-
- 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;
- yaffs_ExtendedTags oldTags;
-
- __u8 *buffer = NULL;
- YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1];
-
- yaffs_ObjectHeader *oh = NULL;
-
- yaffs_strcpy(oldName,_Y("silly old name"));
-
-
- if (!in->fake ||
- in == dev->rootDir || /* The rootDir should also be saved */
- force) {
-
- yaffs_CheckGarbageCollection(dev);
- yaffs_CheckObjectDetailsLoaded(in);
-
- buffer = yaffs_GetTempBuffer(in->myDev, __LINE__);
- oh = (yaffs_ObjectHeader *) buffer;
-
- prevChunkId = in->hdrChunk;
-
- if (prevChunkId > 0) {
- result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId,
- buffer, &oldTags);
-
- yaffs_VerifyObjectHeader(in,oh,&oldTags,0);
-
- memcpy(oldName, oh->name, sizeof(oh->name));
- }
-
- memset(buffer, 0xFF, dev->nDataBytesPerChunk);
-
- oh->type = in->variantType;
- oh->yst_mode = in->yst_mode;
- oh->shadowsObject = oh->inbandShadowsObject = 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>=0) {
- 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;
-
- yaffs_VerifyObjectHeader(in,oh,&newTags,1);
-
- /* Create new chunk in NAND */
- newChunkId =
- yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
- (prevChunkId >= 0) ? 1 : 0);
-
- if (newChunkId >= 0) {
-
- in->hdrChunk = newChunkId;
-
- if (prevChunkId >= 0) {
- yaffs_DeleteChunk(dev, prevChunkId, 1,
- __LINE__);
- }
-
- if(!yaffs_ObjectHasCachedWriteData(in))
- in->dirty = 0;
-
- /* If this was a shrink, then mark the block that the chunk lives on */
- if (isShrink) {
- bi = yaffs_GetBlockInfo(in->myDev,
- newChunkId /in->myDev-> nChunksPerBlock);
- bi->hasShrinkHeader = 1;
- }
-
- }
-
- retVal = newChunkId;
-
- }
-
- if (buffer)
- yaffs_ReleaseTempBuffer(dev, buffer, __LINE__);
-
- return retVal;
-}
-
-/*------------------------ Short Operations Cache ----------------------------------------
- * In many situations where there is no high level buffering (eg WinCE) a lot of
- * reads might be short sequential reads, and a lot of writes may be short
- * sequential writes. eg. scanning/writing a jpeg file.
- * In these cases, a short read/write cache can provide a huge perfomance benefit
- * with dumb-as-a-rock code.
- * In Linux, the page cache provides read buffering aand the short op cache provides write
- * buffering.
- *
- * There are a limited number (~10) of cache chunks per device so that we don't
- * need a very intelligent search.
- */
-
-static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj)
-{
- yaffs_Device *dev = obj->myDev;
- int i;
- yaffs_ChunkCache *cache;
- int nCaches = obj->myDev->nShortOpCaches;
-
- for(i = 0; i < nCaches; i++){
- cache = &dev->srCache[i];
- if (cache->object == obj &&
- cache->dirty)
- return 1;
- }
-
- return 0;
-}
-
-
-static void yaffs_FlushFilesChunkCache(yaffs_Object * obj)
-{
- yaffs_Device *dev = obj->myDev;
- int lowest = -99; /* Stop compiler whining. */
- int i;
- yaffs_ChunkCache *cache;
- int chunkWritten = 0;
- int nCaches = obj->myDev->nShortOpCaches;
-
- if (nCaches > 0) {
- do {
- cache = NULL;
-
- /* Find the dirty cache for this object with the lowest chunk id. */
- for (i = 0; i < nCaches; i++) {
- if (dev->srCache[i].object == obj &&
- dev->srCache[i].dirty) {
- if (!cache
- || dev->srCache[i].chunkId <
- lowest) {
- cache = &dev->srCache[i];
- lowest = cache->chunkId;