- * 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 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) {
- yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId,
- buffer, NULL);
- memcpy(oldName, oh->name, sizeof(oh->name));
- }
-
- memset(buffer, 0xFF, dev->nBytesPerChunk);
-
- 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__);
- }
-
- 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 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;
- }
- }
- }
-
- if (cache && !cache->locked) {
- /* Write it out and free it up */
-
- chunkWritten =
- yaffs_WriteChunkDataToObject(cache->object,
- cache->chunkId,
- cache->data,
- cache->nBytes,
- 1);
- cache->dirty = 0;
- cache->object = NULL;
- }
-
- } while (cache && chunkWritten > 0);
-
- if (cache) {
- /* Hoosterman, disk full while writing cache out. */
- T(YAFFS_TRACE_ERROR,
- (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
-
- }
- }
-
-}
-
-/* Grab us a cache chunk for use.
- * First look for an empty one.
- * Then look for the least recently used non-dirty one.
- * Then look for the least recently used dirty one...., flush and look again.
- */
-static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device * dev)
-{
- int i;
- int usage;
- int theOne;
-
- if (dev->nShortOpCaches > 0) {
- for (i = 0; i < dev->nShortOpCaches; i++) {
- if (!dev->srCache[i].object)
- return &dev->srCache[i];
- }
-
- return NULL;
-
- theOne = -1;
- usage = 0; /* just to stop the compiler grizzling */
-
- for (i = 0; i < dev->nShortOpCaches; i++) {
- if (!dev->srCache[i].dirty &&
- ((dev->srCache[i].lastUse < usage && theOne >= 0) ||
- theOne < 0)) {
- usage = dev->srCache[i].lastUse;
- theOne = i;
- }
- }
-
-
- return theOne >= 0 ? &dev->srCache[theOne] : NULL;
- } else {
- return NULL;
- }
-
-}
-
-static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device * dev)
-{
- yaffs_ChunkCache *cache;
- yaffs_Object *theObj;
- int usage;
- int i;
- int pushout;
-
- if (dev->nShortOpCaches > 0) {
- /* Try find a non-dirty one... */
-
- cache = yaffs_GrabChunkCacheWorker(dev);
-
- if (!cache) {
- /* They were all dirty, find the last recently used object and flush
- * its cache, then find again.
- * NB what's here is not very accurate, we actually flush the object
- * the last recently used page.
- */
-
- /* With locking we can't assume we can use entry zero */
-
- theObj = NULL;
- usage = -1;
- cache = NULL;
- pushout = -1;
-
- for (i = 0; i < dev->nShortOpCaches; i++) {
- if (dev->srCache[i].object &&
- !dev->srCache[i].locked &&
- (dev->srCache[i].lastUse < usage || !cache))
- {
- usage = dev->srCache[i].lastUse;
- theObj = dev->srCache[i].object;
- cache = &dev->srCache[i];
- pushout = i;
- }
- }
-
- if (!cache || cache->dirty) {
- /* Flush and try again */
- yaffs_FlushFilesChunkCache(theObj);
- cache = yaffs_GrabChunkCacheWorker(dev);
- }
-
- }
- return cache;
- } else
- return NULL;
-
-}
-
-/* Find a cached chunk */
-static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object * obj,
- int chunkId)
-{
- yaffs_Device *dev = obj->myDev;
- int i;
- if (dev->nShortOpCaches > 0) {
- for (i = 0; i < dev->nShortOpCaches; i++) {
- if (dev->srCache[i].object == obj &&
- dev->srCache[i].chunkId == chunkId) {
- dev->cacheHits++;
-
- return &dev->srCache[i];
- }
- }
- }
- return NULL;
-}
-
-/* Mark the chunk for the least recently used algorithym */
-static void yaffs_UseChunkCache(yaffs_Device * dev, yaffs_ChunkCache * cache,
- int isAWrite)
-{
-
- if (dev->nShortOpCaches > 0) {
- if (dev->srLastUse < 0 || dev->srLastUse > 100000000) {
- /* Reset the cache usages */
- int i;
- for (i = 1; i < dev->nShortOpCaches; i++) {
- dev->srCache[i].lastUse = 0;
- }
- dev->srLastUse = 0;
- }
-
- dev->srLastUse++;
-
- cache->lastUse = dev->srLastUse;
-
- if (isAWrite) {
- cache->dirty = 1;
- }
- }
-}
-
-/* Invalidate a single cache page.
- * Do this when a whole page gets written,
- * ie the short cache for this page is no longer valid.
- */
-static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId)
-{
- if (object->myDev->nShortOpCaches > 0) {
- yaffs_ChunkCache *cache = yaffs_FindChunkCache(object, chunkId);
-
- if (cache) {
- cache->object = NULL;
- }
- }
-}
-
-/* Invalidate all the cache pages associated with this object
- * Do this whenever ther file is deleted or resized.
- */
-static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in)
-{
- int i;
- yaffs_Device *dev = in->myDev;
-
- if (dev->nShortOpCaches > 0) {
- /* Invalidate it. */
- for (i = 0; i < dev->nShortOpCaches; i++) {
- if (dev->srCache[i].object == in) {
- dev->srCache[i].object = NULL;
- }
- }
- }
-}
-
-/*--------------------- 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)
-{
-
- int chunk;
- int start;
- int nToCopy;
- int n = nBytes;
- int nDone = 0;
- yaffs_ChunkCache *cache;
-
- yaffs_Device *dev;
-
- dev = in->myDev;
-
- while (n > 0) {
- chunk = offset / dev->nBytesPerChunk + 1; /* The first chunk is 1 */
- start = offset % dev->nBytesPerChunk;
-
- /* OK now check for the curveball where the start and end are in
- * the same chunk.
- */
- if ((start + n) < dev->nBytesPerChunk) {
- nToCopy = n;
- } else {
- nToCopy = dev->nBytesPerChunk - start;
- }
-
- cache = yaffs_FindChunkCache(in, chunk);
-
- /* If the chunk is already in the cache or it is less than a whole chunk
- * then use the cache (if there is caching)
- * else bypass the cache.
- */
- if (cache || nToCopy != dev->nBytesPerChunk) {
- if (dev->nShortOpCaches > 0) {
-
- /* If we can't find the data in the cache, then load it up. */
-
- if (!cache) {
- cache = yaffs_GrabChunkCache(in->myDev);
- cache->object = in;
- cache->chunkId = chunk;
- cache->dirty = 0;
- cache->locked = 0;
- yaffs_ReadChunkDataFromObject(in, chunk,
- cache->
- data);
- cache->nBytes = 0;
- }
-
- yaffs_UseChunkCache(dev, cache, 0);
-
- cache->locked = 1;
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_UnlockYAFFS(TRUE);
-#endif
- memcpy(buffer, &cache->data[start], nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_LockYAFFS(TRUE);
-#endif
- cache->locked = 0;
- } else {
- /* Read into the local buffer then copy..*/
-
- __u8 *localBuffer =
- yaffs_GetTempBuffer(dev, __LINE__);
- yaffs_ReadChunkDataFromObject(in, chunk,
- localBuffer);
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_UnlockYAFFS(TRUE);
-#endif
- memcpy(buffer, &localBuffer[start], nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_LockYAFFS(TRUE);
-#endif
- yaffs_ReleaseTempBuffer(dev, localBuffer,
- __LINE__);
- }
-
- } else {
-#ifdef CONFIG_YAFFS_WINCE
- __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
-
- /* Under WinCE can't do direct transfer. Need to use a local buffer.
- * This is because we otherwise screw up WinCE's memory mapper
- */
- yaffs_ReadChunkDataFromObject(in, chunk, localBuffer);
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_UnlockYAFFS(TRUE);
-#endif
- memcpy(buffer, localBuffer, dev->nBytesPerChunk);
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_LockYAFFS(TRUE);
- yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
-#endif
-
-#else
- /* A full chunk. Read directly into the supplied buffer. */
- yaffs_ReadChunkDataFromObject(in, chunk, buffer);
-#endif
- }
-
- n -= nToCopy;
- offset += nToCopy;
- buffer += nToCopy;
- nDone += nToCopy;
-
- }
-
- return nDone;
-}
-
-int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, __u32 offset,
- int nBytes, int writeThrough)
-{
-
- int chunk;
- int start;
- int nToCopy;
- int n = nBytes;
- int nDone = 0;
- int nToWriteBack;
- int startOfWrite = offset;
- int chunkWritten = 0;
- int nBytesRead;
-
- yaffs_Device *dev;
-
- dev = in->myDev;
-
- while (n > 0 && chunkWritten >= 0) {
- chunk = offset / dev->nBytesPerChunk + 1;
- start = offset % dev->nBytesPerChunk;
-
- /* OK now check for the curveball where the start and end are in
- * the same chunk.
- */
-
- if ((start + n) < dev->nBytesPerChunk) {
- nToCopy = n;
-
- /* Now folks, to calculate how many bytes to write back....
- * If we're overwriting and not writing to then end of file then
- * we need to write back as much as was there before.
- */
-
- nBytesRead =
- in->variant.fileVariant.fileSize -
- ((chunk - 1) * dev->nBytesPerChunk);
-
- if (nBytesRead > dev->nBytesPerChunk) {
- nBytesRead = dev->nBytesPerChunk;
- }
-
- nToWriteBack =
- (nBytesRead >
- (start + n)) ? nBytesRead : (start + n);
-
- } else {
- nToCopy = dev->nBytesPerChunk - start;
- nToWriteBack = dev->nBytesPerChunk;
- }
-
- if (nToCopy != dev->nBytesPerChunk) {
- /* An incomplete start or end chunk (or maybe both start and end chunk) */
- if (dev->nShortOpCaches > 0) {
- yaffs_ChunkCache *cache;
- /* If we can't find the data in the cache, then load the cache */
- cache = yaffs_FindChunkCache(in, chunk);
- if (!cache
- && yaffs_CheckSpaceForAllocation(in->
- myDev)) {
- cache = yaffs_GrabChunkCache(in->myDev);
- cache->object = in;
- cache->chunkId = chunk;
- cache->dirty = 0;
- cache->locked = 0;
- yaffs_ReadChunkDataFromObject(in, chunk,
- cache->
- data);
- }
-
- if (cache) {
- yaffs_UseChunkCache(dev, cache, 1);
- cache->locked = 1;
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_UnlockYAFFS(TRUE);
-#endif
-
- memcpy(&cache->data[start], buffer,
- nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_LockYAFFS(TRUE);
-#endif
- cache->locked = 0;
- cache->nBytes = nToWriteBack;
-
- if (writeThrough) {
- chunkWritten =
- yaffs_WriteChunkDataToObject
- (cache->object,
- cache->chunkId,
- cache->data, cache->nBytes,
- 1);
- cache->dirty = 0;
- }
-
- } else {
- chunkWritten = -1; /* fail the write */
- }
- } else {
- /* 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.
- */
-
- __u8 *localBuffer =
- yaffs_GetTempBuffer(dev, __LINE__);
-
- yaffs_ReadChunkDataFromObject(in, chunk,
- localBuffer);
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_UnlockYAFFS(TRUE);
-#endif
-
- memcpy(&localBuffer[start], buffer, nToCopy);
-
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_LockYAFFS(TRUE);
-#endif
- chunkWritten =
- yaffs_WriteChunkDataToObject(in, chunk,
- localBuffer,
- nToWriteBack,
- 0);
-
- yaffs_ReleaseTempBuffer(dev, localBuffer,
- __LINE__);
-
- }
-
- } else {
-
-#ifdef CONFIG_YAFFS_WINCE
- /* Under WinCE can't do direct transfer. Need to use a local buffer.
- * This is because we otherwise screw up WinCE's memory mapper
- */
- __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_UnlockYAFFS(TRUE);
-#endif
- memcpy(localBuffer, buffer, dev->nBytesPerChunk);
-#ifdef CONFIG_YAFFS_WINCE
- yfsd_LockYAFFS(TRUE);
-#endif
- chunkWritten =
- yaffs_WriteChunkDataToObject(in, chunk, localBuffer,
- dev->nBytesPerChunk,
- 0);
- yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
-#else
- /* A full chunk. Write directly from the supplied buffer. */
- chunkWritten =
- yaffs_WriteChunkDataToObject(in, chunk, buffer,
- dev->nBytesPerChunk,
- 0);
-#endif
- /* Since we've overwritten the cached data, we better invalidate it. */
- yaffs_InvalidateChunkCache(in, chunk);
- }