+ 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 %p" TENDSTR),
+ cp.objectId, cp.parentId, cp.variantType, cp.hdrChunk, 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, (int)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) {
+ cache = yaffs_GrabChunkCache(in->myDev);
+ cache->object = in;
+ cache->chunkId = chunk;