+
+ }
+
+ 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);
+ }
+
+ }
+
+}
+
+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;
+ }
+ }
+ }
+
+ 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)));
+
+ }
+ }
+
+}
+
+/*yaffs_FlushEntireDeviceCache(dev)
+ *
+ *
+ */
+
+void yaffs_FlushEntireDeviceCache(yaffs_Device *dev)
+{
+ yaffs_Object *obj;
+ int nCaches = dev->nShortOpCaches;
+ int i;
+
+ /* Find a dirty object in the cache and flush it...
+ * until there are no further dirty objects.
+ */
+ do {
+ obj = NULL;
+ for (i = 0; i < nCaches && !obj; i++) {
+ if (dev->srCache[i].object &&
+ dev->srCache[i].dirty)
+ obj = dev->srCache[i].object;
+
+ }
+ if (obj)
+ yaffs_FlushFilesChunkCache(obj);
+
+ } while (obj);
+
+}
+
+
+/* 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;
+
+ if (dev->nShortOpCaches > 0) {
+ for (i = 0; i < dev->nShortOpCaches; i++) {
+ if (!dev->srCache[i].object)
+ return &dev->srCache[i];
+ }
+ }
+
+ 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;
+ }
+ }
+}
+
+/*--------------------- Checkpointing --------------------*/
+
+
+static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev, int head)
+{
+ yaffs_CheckpointValidity cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.structType = sizeof(cp);
+ cp.magic = YAFFS_MAGIC;
+ cp.version = YAFFS_CHECKPOINT_VERSION;
+ cp.head = (head) ? 1 : 0;
+
+ return (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp)) ?
+ 1 : 0;
+}
+
+static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head)
+{
+ yaffs_CheckpointValidity cp;
+ int ok;
+
+ ok = (yaffs_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp));
+
+ if (ok)
+ ok = (cp.structType == sizeof(cp)) &&
+ (cp.magic == YAFFS_MAGIC) &&
+ (cp.version == YAFFS_CHECKPOINT_VERSION) &&
+ (cp.head == ((head) ? 1 : 0));
+ return ok ? 1 : 0;
+}
+
+static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp,
+ yaffs_Device *dev)
+{
+ cp->nErasedBlocks = dev->nErasedBlocks;
+ cp->allocationBlock = dev->allocationBlock;
+ cp->allocationPage = dev->allocationPage;
+ cp->nFreeChunks = dev->nFreeChunks;
+
+ cp->nDeletedFiles = dev->nDeletedFiles;
+ cp->nUnlinkedFiles = dev->nUnlinkedFiles;
+ cp->nBackgroundDeletions = dev->nBackgroundDeletions;
+ cp->sequenceNumber = dev->sequenceNumber;
+ cp->oldestDirtySequence = dev->oldestDirtySequence;
+
+}
+
+static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev,
+ yaffs_CheckpointDevice *cp)
+{
+ dev->nErasedBlocks = cp->nErasedBlocks;
+ dev->allocationBlock = cp->allocationBlock;
+ dev->allocationPage = cp->allocationPage;
+ dev->nFreeChunks = cp->nFreeChunks;
+
+ dev->nDeletedFiles = cp->nDeletedFiles;
+ dev->nUnlinkedFiles = cp->nUnlinkedFiles;
+ dev->nBackgroundDeletions = cp->nBackgroundDeletions;
+ dev->sequenceNumber = cp->sequenceNumber;
+ dev->oldestDirtySequence = cp->oldestDirtySequence;
+}
+
+
+static int yaffs_WriteCheckpointDevice(yaffs_Device *dev)
+{
+ yaffs_CheckpointDevice cp;
+ __u32 nBytes;
+ __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1);
+
+ int ok;
+
+ /* Write device runtime values*/
+ yaffs_DeviceToCheckpointDevice(&cp, dev);
+ cp.structType = sizeof(cp);
+
+ ok = (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp));
+
+ /* Write block info */
+ if (ok) {
+ nBytes = nBlocks * sizeof(yaffs_BlockInfo);
+ ok = (yaffs_CheckpointWrite(dev, dev->blockInfo, nBytes) == nBytes);
+ }
+
+ /* Write chunk bits */
+ if (ok) {
+ nBytes = nBlocks * dev->chunkBitmapStride;
+ ok = (yaffs_CheckpointWrite(dev, dev->chunkBits, nBytes) == nBytes);
+ }
+ return ok ? 1 : 0;
+
+}
+
+static int yaffs_ReadCheckpointDevice(yaffs_Device *dev)
+{
+ yaffs_CheckpointDevice cp;
+ __u32 nBytes;
+ __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1);
+
+ int ok;
+
+ ok = (yaffs_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp));
+ if (!ok)
+ return 0;
+
+ if (cp.structType != sizeof(cp))
+ return 0;
+
+
+ yaffs_CheckpointDeviceToDevice(dev, &cp);
+
+ nBytes = nBlocks * sizeof(yaffs_BlockInfo);
+
+ ok = (yaffs_CheckpointRead(dev, dev->blockInfo, nBytes) == nBytes);
+
+ if (!ok)
+ return 0;
+ nBytes = nBlocks * dev->chunkBitmapStride;
+
+ ok = (yaffs_CheckpointRead(dev, dev->chunkBits, nBytes) == nBytes);
+
+ return ok ? 1 : 0;
+}
+
+static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject *cp,
+ yaffs_Object *obj)
+{
+
+ cp->objectId = obj->objectId;
+ cp->parentId = (obj->parent) ? obj->parent->objectId : 0;
+ cp->hdrChunk = obj->hdrChunk;
+ cp->variantType = obj->variantType;
+ cp->deleted = obj->deleted;
+ cp->softDeleted = obj->softDeleted;
+ cp->unlinked = obj->unlinked;
+ cp->fake = obj->fake;
+ cp->renameAllowed = obj->renameAllowed;
+ cp->unlinkAllowed = obj->unlinkAllowed;
+ cp->serial = obj->serial;
+ cp->nDataChunks = obj->nDataChunks;
+
+ if (obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+ cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize;
+ else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+ cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId;
+}
+
+static int yaffs_CheckpointObjectToObject(yaffs_Object *obj, yaffs_CheckpointObject *cp)
+{
+
+ yaffs_Object *parent;
+
+ if (obj->variantType != cp->variantType) {
+ T(YAFFS_TRACE_ERROR, (TSTR("Checkpoint read object %d type %d "
+ TCONT("chunk %d does not match existing object type %d")
+ TENDSTR), cp->objectId, cp->variantType, cp->hdrChunk,
+ obj->variantType));
+ return 0;
+ }
+
+ obj->objectId = cp->objectId;
+
+ if (cp->parentId)
+ parent = yaffs_FindOrCreateObjectByNumber(
+ obj->myDev,
+ cp->parentId,
+ YAFFS_OBJECT_TYPE_DIRECTORY);
+ else
+ parent = NULL;
+
+ if (parent) {
+ if (parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) {
+ T(YAFFS_TRACE_ALWAYS, (TSTR("Checkpoint read object %d parent %d type %d"
+ TCONT(" chunk %d Parent type, %d, not directory")
+ TENDSTR),
+ cp->objectId, cp->parentId, cp->variantType,
+ cp->hdrChunk, parent->variantType));
+ return 0;
+ }
+ yaffs_AddObjectToDirectory(parent, obj);
+ }
+
+ obj->hdrChunk = cp->hdrChunk;
+ obj->variantType = cp->variantType;
+ obj->deleted = cp->deleted;
+ obj->softDeleted = cp->softDeleted;
+ obj->unlinked = cp->unlinked;
+ obj->fake = cp->fake;
+ obj->renameAllowed = cp->renameAllowed;
+ obj->unlinkAllowed = cp->unlinkAllowed;
+ obj->serial = cp->serial;
+ obj->nDataChunks = cp->nDataChunks;
+
+ if (obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+ obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId;
+ else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+ obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId;
+
+ if (obj->hdrChunk > 0)
+ obj->lazyLoaded = 1;
+ return 1;
+}
+
+
+
+static int yaffs_CheckpointTnodeWorker(yaffs_Object *in, yaffs_Tnode *tn,
+ __u32 level, int chunkOffset)
+{
+ int i;
+ yaffs_Device *dev = in->myDev;
+ int ok = 1;
+ int tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+ if (tnodeSize < sizeof(yaffs_Tnode))
+ tnodeSize = sizeof(yaffs_Tnode);
+
+
+ if (tn) {
+ if (level > 0) {
+
+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) {
+ if (tn->internal[i]) {
+ ok = yaffs_CheckpointTnodeWorker(in,
+ tn->internal[i],
+ level - 1,
+ (chunkOffset<<YAFFS_TNODES_INTERNAL_BITS) + i);
+ }
+ }
+ } else if (level == 0) {
+ __u32 baseOffset = chunkOffset << YAFFS_TNODES_LEVEL0_BITS;
+ ok = (yaffs_CheckpointWrite(dev, &baseOffset, sizeof(baseOffset)) == sizeof(baseOffset));
+ if (ok)
+ ok = (yaffs_CheckpointWrite(dev, tn, tnodeSize) == tnodeSize);
+ }
+ }
+
+ return ok;
+
+}
+
+static int yaffs_WriteCheckpointTnodes(yaffs_Object *obj)
+{
+ __u32 endMarker = ~0;
+ int ok = 1;
+
+ if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) {
+ ok = yaffs_CheckpointTnodeWorker(obj,
+ obj->variant.fileVariant.top,
+ obj->variant.fileVariant.topLevel,
+ 0);
+ if (ok)
+ ok = (yaffs_CheckpointWrite(obj->myDev, &endMarker, sizeof(endMarker)) ==
+ sizeof(endMarker));
+ }
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj)
+{
+ __u32 baseChunk;
+ int ok = 1;
+ yaffs_Device *dev = obj->myDev;
+ yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant;
+ yaffs_Tnode *tn;
+ int nread = 0;
+ int tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+ if (tnodeSize < sizeof(yaffs_Tnode))
+ tnodeSize = sizeof(yaffs_Tnode);
+
+ ok = (yaffs_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk));
+
+ while (ok && (~baseChunk)) {
+ nread++;
+ /* Read level 0 tnode */
+
+
+ tn = yaffs_GetTnodeRaw(dev);
+ if (tn)
+ ok = (yaffs_CheckpointRead(dev, tn, tnodeSize) == tnodeSize);
+ else
+ ok = 0;
+
+ if (tn && ok)
+ ok = yaffs_AddOrFindLevel0Tnode(dev,
+ fileStructPtr,
+ baseChunk,
+ tn) ? 1 : 0;
+
+ if (ok)
+ ok = (yaffs_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk));
+
+ }
+
+ T(YAFFS_TRACE_CHECKPOINT, (
+ TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR),
+ nread, baseChunk, ok));
+
+ return ok ? 1 : 0;
+}
+
+
+static int yaffs_WriteCheckpointObjects(yaffs_Device *dev)
+{
+ yaffs_Object *obj;
+ yaffs_CheckpointObject cp;
+ int i;
+ int ok = 1;
+ struct ylist_head *lh;
+
+
+ /* Iterate through the objects in each hash entry,
+ * dumping them to the checkpointing stream.
+ */
+
+ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) {
+ ylist_for_each(lh, &dev->objectBucket[i].list) {
+ if (lh) {
+ obj = ylist_entry(lh, yaffs_Object, hashLink);
+ if (!obj->deferedFree) {
+ yaffs_ObjectToCheckpointObject(&cp, obj);
+ cp.structType = sizeof(cp);
+
+ T(YAFFS_TRACE_CHECKPOINT, (
+ TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR),
+ cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk, (unsigned) obj));
+
+ ok = (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp));
+
+ if (ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+ ok = yaffs_WriteCheckpointTnodes(obj);
+ }
+ }
+ }
+ }
+
+ /* Dump end of list */
+ memset(&cp, 0xFF, sizeof(yaffs_CheckpointObject));
+ cp.structType = sizeof(cp);
+
+ if (ok)
+ ok = (yaffs_CheckpointWrite(dev, &cp, sizeof(cp)) == sizeof(cp));
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs_ReadCheckpointObjects(yaffs_Device *dev)
+{
+ yaffs_Object *obj;
+ yaffs_CheckpointObject cp;
+ int ok = 1;
+ int done = 0;
+ yaffs_Object *hardList = NULL;
+
+ while (ok && !done) {
+ ok = (yaffs_CheckpointRead(dev, &cp, sizeof(cp)) == sizeof(cp));
+ if (cp.structType != sizeof(cp)) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("struct size %d instead of %d ok %d"TENDSTR),
+ cp.structType, sizeof(cp), ok));
+ ok = 0;
+ }
+
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("Checkpoint read object %d parent %d type %d chunk %d " TENDSTR),
+ cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk));
+
+ if (ok && cp.objectId == ~0)
+ done = 1;
+ else if (ok) {
+ obj = yaffs_FindOrCreateObjectByNumber(dev, cp.objectId, cp.variantType);
+ if (obj) {
+ ok = yaffs_CheckpointObjectToObject(obj, &cp);
+ if (!ok)
+ break;
+ if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) {
+ ok = yaffs_ReadCheckpointTnodes(obj);
+ } else if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) {
+ obj->hardLinks.next =
+ (struct ylist_head *) hardList;
+ hardList = obj;
+ }
+ } else
+ ok = 0;
+ }
+ }
+
+ if (ok)
+ yaffs_HardlinkFixup(dev, hardList);
+
+ return ok ? 1 : 0;
+}
+
+static int yaffs_WriteCheckpointSum(yaffs_Device *dev)
+{
+ __u32 checkpointSum;
+ int ok;
+
+ yaffs_GetCheckpointSum(dev, &checkpointSum);
+
+ ok = (yaffs_CheckpointWrite(dev, &checkpointSum, sizeof(checkpointSum)) == sizeof(checkpointSum));
+
+ if (!ok)
+ return 0;
+
+ return 1;
+}
+
+static int yaffs_ReadCheckpointSum(yaffs_Device *dev)
+{
+ __u32 checkpointSum0;
+ __u32 checkpointSum1;
+ int ok;
+
+ yaffs_GetCheckpointSum(dev, &checkpointSum0);
+
+ ok = (yaffs_CheckpointRead(dev, &checkpointSum1, sizeof(checkpointSum1)) == sizeof(checkpointSum1));
+
+ if (!ok)
+ return 0;
+
+ if (checkpointSum0 != checkpointSum1)
+ return 0;
+
+ return 1;
+}
+
+
+static int yaffs_WriteCheckpointData(yaffs_Device *dev)
+{
+ int ok = 1;
+
+ if (dev->skipCheckpointWrite || !dev->isYaffs2) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint write" TENDSTR)));
+ ok = 0;
+ }
+
+ if (ok)
+ ok = yaffs_CheckpointOpen(dev, 1);
+
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint validity" TENDSTR)));
+ ok = yaffs_WriteCheckpointValidityMarker(dev, 1);
+ }
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint device" TENDSTR)));
+ ok = yaffs_WriteCheckpointDevice(dev);
+ }
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint objects" TENDSTR)));
+ ok = yaffs_WriteCheckpointObjects(dev);
+ }
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("write checkpoint validity" TENDSTR)));
+ ok = yaffs_WriteCheckpointValidityMarker(dev, 0);
+ }
+
+ if (ok)
+ ok = yaffs_WriteCheckpointSum(dev);
+
+ if (!yaffs_CheckpointClose(dev))
+ ok = 0;
+
+ if (ok)
+ dev->isCheckpointed = 1;
+ else
+ dev->isCheckpointed = 0;
+
+ return dev->isCheckpointed;
+}
+
+static int yaffs_ReadCheckpointData(yaffs_Device *dev)
+{
+ int ok = 1;
+
+ if (dev->skipCheckpointRead || !dev->isYaffs2) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint read" TENDSTR)));
+ ok = 0;
+ }
+
+ if (ok)
+ ok = yaffs_CheckpointOpen(dev, 0); /* open for read */
+
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint validity" TENDSTR)));
+ ok = yaffs_ReadCheckpointValidityMarker(dev, 1);
+ }
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint device" TENDSTR)));
+ ok = yaffs_ReadCheckpointDevice(dev);
+ }
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint objects" TENDSTR)));
+ ok = yaffs_ReadCheckpointObjects(dev);
+ }
+ if (ok) {
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint validity" TENDSTR)));
+ ok = yaffs_ReadCheckpointValidityMarker(dev, 0);
+ }
+
+ if (ok) {
+ ok = yaffs_ReadCheckpointSum(dev);
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("read checkpoint checksum %d" TENDSTR), ok));
+ }
+
+ if (!yaffs_CheckpointClose(dev))
+ ok = 0;
+
+ if (ok)
+ dev->isCheckpointed = 1;
+ else
+ dev->isCheckpointed = 0;
+
+ return ok ? 1 : 0;
+
+}
+
+static void yaffs_InvalidateCheckpoint(yaffs_Device *dev)
+{
+ if (dev->isCheckpointed ||
+ dev->blocksInCheckpoint > 0) {
+ dev->isCheckpointed = 0;
+ yaffs_CheckpointInvalidateStream(dev);
+ if (dev->superBlock && dev->markSuperBlockDirty)
+ dev->markSuperBlockDirty(dev->superBlock);
+ }
+}
+
+
+int yaffs_CheckpointSave(yaffs_Device *dev)
+{
+
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("save entry: isCheckpointed %d"TENDSTR), dev->isCheckpointed));
+
+ yaffs_VerifyObjects(dev);
+ yaffs_VerifyBlocks(dev);
+ yaffs_VerifyFreeChunks(dev);
+
+ if (!dev->isCheckpointed) {
+ yaffs_InvalidateCheckpoint(dev);
+ yaffs_WriteCheckpointData(dev);
+ }
+
+ T(YAFFS_TRACE_ALWAYS, (TSTR("save exit: isCheckpointed %d"TENDSTR), dev->isCheckpointed));
+
+ return dev->isCheckpointed;
+}
+
+int yaffs_CheckpointRestore(yaffs_Device *dev)
+{
+ int retval;
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("restore entry: isCheckpointed %d"TENDSTR), dev->isCheckpointed));
+
+ retval = yaffs_ReadCheckpointData(dev);
+
+ if (dev->isCheckpointed) {
+ yaffs_VerifyObjects(dev);
+ yaffs_VerifyBlocks(dev);
+ yaffs_VerifyFreeChunks(dev);
+ }
+
+ T(YAFFS_TRACE_CHECKPOINT, (TSTR("restore exit: isCheckpointed %d"TENDSTR), dev->isCheckpointed));
+
+ return retval;
+}
+
+/*--------------------- 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, loff_t offset,
+ int nBytes)
+{
+
+ int chunk;
+ __u32 start;
+ int nToCopy;
+ int n = nBytes;
+ int nDone = 0;
+ yaffs_ChunkCache *cache;
+
+ yaffs_Device *dev;
+
+ dev = in->myDev;
+
+ while (n > 0) {
+ /* chunk = offset / dev->nDataBytesPerChunk + 1; */
+ /* start = offset % dev->nDataBytesPerChunk; */
+ yaffs_AddrToChunk(dev, offset, &chunk, &start);
+ chunk++;
+
+ /* OK now check for the curveball where the start and end are in
+ * the same chunk.
+ */
+ if ((start + n) < dev->nDataBytesPerChunk)
+ nToCopy = n;
+ else
+ nToCopy = dev->nDataBytesPerChunk - start;
+
+ cache = yaffs_FindChunkCache(in, chunk);
+
+ /* If the chunk is already in the cache or it is less than a whole chunk
+ * or we're using inband tags then use the cache (if there is caching)
+ * else bypass the cache.
+ */
+ if (cache || nToCopy != dev->nDataBytesPerChunk || dev->inbandTags) {
+ if (dev->nShortOpCaches > 0) {
+
+ /* If we can't find the data in the cache, then load it up. */
+
+ if (!cache) {