changeset 127:f8fc631acf25

Checkpointing changes
author charles <charles>
date Mon, 08 May 2006 10:13:34 +0000
parents 2e7fd6d40037
children 994356a93534
files direct/Makefile direct/dtest.c direct/yaffscfg2k.c direct/yaffsfs.c yaffs_checkptrw.c yaffs_checkptrw.h yaffs_fs.c yaffs_guts.c yaffs_guts.h yaffs_nand.c yaffs_nand.h yportenv.h
diffstat 12 files changed, 1258 insertions(+), 251 deletions(-) [+]
line wrap: on
line diff
--- a/direct/Makefile
+++ b/direct/Makefile
@@ -10,7 +10,7 @@
 #
 # NB Warning this Makefile does not include header dependencies.
 #
-# $Id: Makefile,v 1.8 2005-08-09 01:02:04 charles Exp $
+# $Id: Makefile,v 1.9 2006-05-08 10:13:34 charles Exp $
 
 EXTRA_COMPILE_FLAGS = -DYAFFS_IGNORE_TAGS_ECC
 
@@ -19,7 +19,12 @@ CFLAGS =    -Wall -DCONFIG_YAFFS_DIRECT 
 #CFLAGS+=   -Wmissing-prototypes -Wredundant-decls -Wnested-externs -Winline
 
 
- DIRECTTESTOBJS = dtest.o yaffscfg2k.o yaffs_ecc.o yaffs_fileem2k.o yaffsfs.o yaffs_guts.o yaffs_packedtags1.o yaffs_ramdisk.o yaffs_ramem2k.o yaffs_tagscompat.o yaffs_packedtags2.o yaffs_tagsvalidity.o
+DIRECTTESTOBJS = dtest.o yaffscfg2k.o yaffs_ecc.o yaffs_fileem2k.o yaffsfs.o yaffs_guts.o \
+		 yaffs_packedtags1.o yaffs_ramdisk.o yaffs_ramem2k.o \
+		 yaffs_tagscompat.o yaffs_packedtags2.o yaffs_tagsvalidity.o yaffs_nand.o \
+		 yaffs_checkptrw.o \
+#		 yaffs_checkptrwtest.o\
+		 
 
 BOOTTESTOBJS = bootldtst.o yboot.o yaffs_fileem.o nand_ecc.o
 
@@ -29,7 +34,8 @@ ALLOBJS = $(DIRECTTESTOBJS) $(BOOTTESTOB
 
 SYMLINKS = devextras.h yaffs_ecc.c yaffs_ecc.h yaffs_guts.c yaffs_guts.h yaffsinterface.h yportenv.h yaffs_tagscompat.c yaffs_tagscompat.h \
           yaffs_packedtags1.c yaffs_packedtags1.h yaffs_packedtags2.c yaffs_packedtags2.h  yaffs_nandemul2k.h \
-          yaffs_tagsvalidity.c yaffs_tagsvalidity.h
+          yaffs_nand.c yaffs_nand.h \
+          yaffs_tagsvalidity.c yaffs_tagsvalidity.h yaffs_checkptrw.h yaffs_checkptrw.c
 
 #all: directtest2k boottest
 
--- a/direct/dtest.c
+++ b/direct/dtest.c
@@ -496,7 +496,7 @@ void dumpDirFollow(const char *dname)
 }
 
 
-void dumpDir(const char *dname)
+void dump_directory_tree_worker(const char *dname,int recursive)
 {
 	yaffs_DIR *d;
 	yaffs_dirent *de;
@@ -517,7 +517,7 @@ void dumpDir(const char *dname)
 			
 			yaffs_lstat(str,&s);
 			
-			printf("%s length %d mode %X ",de->d_name,(int)s.st_size,s.st_mode);
+			printf("%s length %d mode %X ",str,(int)s.st_size,s.st_mode);
 			switch(s.st_mode & S_IFMT)
 			{
 				case S_IFREG: printf("data file"); break;
@@ -531,15 +531,29 @@ void dumpDir(const char *dname)
 				default: printf("unknown"); break;
 			}
 			
-			printf("\n");           
+			printf("\n");
+
+			if((s.st_mode & S_IFMT) == S_IFDIR && recursive)
+				dump_directory_tree_worker(str,1);
+							
 		}
 		
 		yaffs_closedir(d);
 	}
+
+}
+
+static void dump_directory_tree(const char *dname)
+{
+	dump_directory_tree_worker(dname,1);
 	printf("\n");
-	
 	printf("Free space in %s is %d\n\n",dname,(int)yaffs_freespace(dname));
+}
 
+void dumpDir(const char *dname)
+{	dump_directory_tree_worker(dname,0);
+	printf("\n");
+	printf("Free space in %s is %d\n\n",dname,(int)yaffs_freespace(dname));
 }
 
 
@@ -1582,18 +1596,18 @@ void write_10k(int h)
 void write_200k_file(const char *fn, const char *fdel, const char *fdel1)
 {
    int h1;
-   int h2;
    int i;
+   int offs;
    
    h1 = yaffs_open(fn, O_CREAT | O_TRUNC | O_RDWR, S_IREAD | S_IWRITE);
    
    for(i = 0; i < 100000; i+= 10000)
    {
    	write_10k(h1);
-   	write_10k(h2);
    }
    
-   if(yaffs_lseek(h1,0,SEEK_SET) != 1000000)
+   offs = yaffs_lseek(h1,0,SEEK_CUR);
+   if( offs != 100000)
    {
    	printf("Could not write file\n");
    }
@@ -1602,19 +1616,17 @@ void write_200k_file(const char *fn, con
    for(i = 0; i < 100000; i+= 10000)
    {
    	write_10k(h1);
-   	write_10k(h2);
    }
    
-   if(yaffs_lseek(h1,0,SEEK_SET) != 2000000)
+   offs = yaffs_lseek(h1,0,SEEK_CUR);
+   if( offs != 200000)
    {
    	printf("Could not write file\n");
    }
    
    yaffs_close(h1);
-   yaffs_close(h2);
    yaffs_unlink(fdel1);
    
-   
 }
 
 
@@ -1624,17 +1636,21 @@ void verify_200k_file(const char *fn)
    int i;
    char x[11];
    const char *s="0123456789";
+   int errCount = 0;
    
    h1 = yaffs_open(fn, O_RDONLY, 0);
    
-   for(i = 0; i < 200000; i+= 10)
+   for(i = 0; i < 200000 && errCount < 10; i+= 10)
    {
    	yaffs_read(h1,x,10);
 	if(strncmp(x,s,10) != 0)
 	{
-		printf("File verification failed at %d\n",i);
+		printf("File %s verification failed at %d\n",fn,i);
+		errCount++;
 	}
    }
+   if(errCount >= 10)
+   	printf("Too many errors... aborted\n");
       
    yaffs_close(h1);	   
 	
@@ -1677,6 +1693,44 @@ void check_resize_gc_bug(const char *mou
 }
 
 
+void multi_mount_test(const char *mountpt,int nmounts)
+{
+
+	char a[30];
+	char b[30];
+	char c[30];
+	
+	int i;
+	int j;
+	
+	sprintf(a,"%s/a",mountpt);
+	
+
+	
+	
+	yaffs_StartUp();
+	
+	for(i = 0; i < nmounts; i++){
+		printf("############### Iteration %d   Start\n",i);
+		yaffs_mount(mountpt);
+		dump_directory_tree(mountpt);
+		yaffs_mkdir(a,0);
+		for(j = 0; j < i; j++){
+			sprintf(b,"%s/%d",a,j);
+			verify_200k_file(b);
+		}
+		sprintf(b,"%s/%d",a,i);
+
+		write_200k_file(b,"","");
+		
+		printf("######## Iteration %d   Start\n",i);
+		dump_directory_tree(mountpt);
+		
+		yaffs_unmount(mountpt);
+	}
+}
+
+
 int main(int argc, char *argv[])
 {
 	//return long_test(argc,argv);
@@ -1688,11 +1742,13 @@ int main(int argc, char *argv[])
 	//huge_directory_test_on_path("/ram2k");
 	
 	 //yaffs_backward_scan_test("/flash/flash");
-	 yaffs_device_flush_test("/flash/flash");
+	// yaffs_device_flush_test("/flash/flash");
 
 	 
 	 //scan_pattern_test("/flash",10000,10);
-	//short_scan_test("/flash",40000,200);
+	//short_scan_test("/flash/flash",40000,200);
+	 multi_mount_test("/flash/flash",20);
+
 
 	
 	//long_test_on_path("/ram2k");
--- a/direct/yaffscfg2k.c
+++ b/direct/yaffscfg2k.c
@@ -125,7 +125,9 @@ int yaffs_StartUp(void)
 	flashDev.nBytesPerChunk = 2048;
 	flashDev.nChunksPerBlock = 64;
 	flashDev.nReservedBlocks = 5;
-	flashDev.startBlock = 0; 
+	flashDev.checkpointStartBlock = 1;
+	flashDev.checkpointEndBlock = 20;
+	flashDev.startBlock = 21; 
 	//flashDev.endBlock = 127; // Last block in 16MB
 	flashDev.endBlock = yflash_GetNumberOfBlocks()-1;
 	flashDev.isYaffs2 = 1;
--- a/direct/yaffsfs.c
+++ b/direct/yaffsfs.c
@@ -25,7 +25,7 @@
 #endif
 
 
-const char *yaffsfs_c_version="$Id: yaffsfs.c,v 1.11 2006-04-21 20:24:35 colin Exp $";
+const char *yaffsfs_c_version="$Id: yaffsfs.c,v 1.12 2006-05-08 10:13:35 charles Exp $";
 
 // configurationList is the list of devices that are supported
 static yaffsfs_DeviceConfiguration *yaffsfs_configurationList;
@@ -1078,6 +1078,7 @@ int yaffs_unmount(const char *path)
 			int inUse;
 			
 			yaffs_FlushEntireDeviceCache(dev);
+			yaffs_CheckpointSave(dev);
 			
 			for(i = inUse = 0; i < YAFFSFS_N_HANDLES && !inUse; i++)
 			{
new file mode 100644
--- /dev/null
+++ b/yaffs_checkptrw.c
@@ -0,0 +1,266 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+const char *yaffs_checkptrw_c_version =
+    "$Id: yaffs_checkptrw.c,v 1.1 2006-05-08 10:13:34 charles Exp $";
+
+
+#include "yaffs_checkptrw.h"
+
+
+int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
+{
+
+	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpt blocks %d %d %d %d" TENDSTR),
+		dev->checkpointStartBlock, dev->checkpointEndBlock,
+		dev->startBlock,dev->endBlock));
+		
+	if(dev->checkpointStartBlock >= dev->checkpointEndBlock)
+		return 0;
+		
+	if(dev->checkpointStartBlock >= dev->startBlock && 
+	   dev->checkpointStartBlock <= dev->endBlock)
+	   	return 0;
+
+	if(dev->checkpointEndBlock >= dev->startBlock && 
+	   dev->checkpointEndBlock <= dev->endBlock)
+	   	return 0;
+	   	
+	return 1;
+}
+
+int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
+{
+	int i;
+	
+	/* Got the functions we need? */
+	if (!dev->writeChunkWithTagsToNAND ||
+	    !dev->readChunkWithTagsFromNAND ||
+	    !dev->eraseBlockInNAND)
+		return 0;
+	
+	/* Got a valid checkpoint data region? */
+	if(!yaffs_CheckpointSpaceOk(dev))
+		return 0;
+			
+	if(!dev->checkpointBuffer)
+		dev->checkpointBuffer = YMALLOC(dev->nBytesPerChunk);
+	if(!dev->checkpointBuffer)
+		return 0;
+	
+	dev->checkpointPage = dev->checkpointStartBlock * dev->nChunksPerBlock;
+	
+	dev->checkpointOpenForWrite = forWriting;
+	
+	dev->checkpointByteCount = 0;
+	
+	/* Erase all the blocks in the checkpoint area */
+	if(forWriting){
+		T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt data"TENDSTR)));
+		for( i = dev->checkpointStartBlock; i <= dev->checkpointEndBlock; i++)
+			dev->eraseBlockInNAND(dev,i);
+		
+		dev->checkpointByteOffset = 0;
+		
+		memset(dev->checkpointBuffer,0,dev->nBytesPerChunk);
+		
+	} else {
+		/* Set to a value that will kick off a read */
+		dev->checkpointByteOffset = dev->nBytesPerChunk;
+	}
+	
+	return 1;
+}
+
+static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
+{
+
+	int blockNo;
+	int goodBlockFound;
+	yaffs_BlockState state;
+	__u32 seqenceNumber;
+
+	yaffs_ExtendedTags tags;
+	tags.chunkDeleted = 0;
+	tags.objectId = YAFFS_OBJECTID_CHECKPOINT_DATA;
+	tags.chunkId = dev->checkpointPage + 1;
+	tags.sequenceNumber = 1;
+	tags.byteCount = dev->nBytesPerChunk;
+	
+	/* printf("write checkpoint page %d\n",dev->checkpointPage); */
+	
+	if(dev->checkpointPage%dev->nChunksPerBlock == 0){
+		/* Start of a new block, do a block validity check */
+		blockNo = dev->checkpointPage/dev->nChunksPerBlock;
+		goodBlockFound = 0;
+		while(blockNo <= dev->checkpointEndBlock && !goodBlockFound){
+			dev->queryNANDBlock(dev,blockNo,&state,&tags);
+			if(state != YAFFS_BLOCK_STATE_DEAD)
+				goodBlockFound = 1;
+			else {
+				blockNo++;
+				dev->checkpointPage += dev->nChunksPerBlock;
+			}
+		}
+		
+		if(!goodBlockFound)
+			return 0;
+	}
+	
+	dev->writeChunkWithTagsToNAND(dev, dev->checkpointPage, 
+				      dev->checkpointBuffer,&tags);
+	dev->checkpointByteOffset = 0;
+	dev->checkpointPage++;	   
+	memset(dev->checkpointBuffer,0,dev->nBytesPerChunk);
+	
+	return 1;
+}
+
+int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
+{
+	int i=0;
+	int ok = 1;
+
+	
+	__u8 * dataBytes = (__u8 *)data;
+	
+	
+
+	if(!dev->checkpointBuffer)
+		return 0;
+
+	while(i < nBytes && ok) {
+		
+
+		
+		 dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
+		dev->checkpointByteOffset++;
+		i++;
+		dataBytes++;
+		dev->checkpointByteCount++;
+		
+		
+		if(dev->checkpointByteOffset < 0 ||
+		   dev->checkpointByteOffset >= dev->nBytesPerChunk) 
+			ok = yaffs_CheckpointFlushBuffer(dev);
+
+	}
+	
+	return 	i;
+}
+
+int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
+{
+	int i=0;
+	int ok = 1;
+	yaffs_ExtendedTags tags;
+	yaffs_BlockState state;
+	int blockNo;
+	int goodBlockFound;
+
+	__u8 *dataBytes = (__u8 *)data;
+		
+	if(!dev->checkpointBuffer)
+		return 0;
+
+	while(i < nBytes && ok) {
+		if(dev->checkpointByteOffset < 0 ||
+		   dev->checkpointByteOffset >= dev->nBytesPerChunk) {
+			if(dev->checkpointPage%dev->nChunksPerBlock == 0){
+				/* Start of a new block, do a block validity check */
+				blockNo = dev->checkpointPage/dev->nChunksPerBlock;
+				goodBlockFound = 0;
+				while(blockNo <= dev->checkpointEndBlock && !goodBlockFound){
+					dev->queryNANDBlock(dev,blockNo,&state,&tags);
+					if(state != YAFFS_BLOCK_STATE_DEAD)
+						goodBlockFound = 1;
+					else {
+						blockNo++;
+						dev->checkpointPage += dev->nChunksPerBlock;
+					}
+				}
+		
+				if(!goodBlockFound)
+					return 0;
+			}
+
+
+	   		/* read in the next chunk */
+	   		/* printf("read checkpoint page %d\n",dev->checkpointPage); */
+			dev->readChunkWithTagsFromNAND(dev, dev->checkpointPage, 
+						       dev->checkpointBuffer,
+						      &tags);
+						      
+			if(tags.objectId != YAFFS_OBJECTID_CHECKPOINT_DATA ||
+			   tags.chunkId != (dev->checkpointPage + 1))
+			   ok = 0;
+
+			dev->checkpointByteOffset = 0;
+			dev->checkpointPage++;
+		}
+		
+		if(ok){
+			*dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
+			dev->checkpointByteOffset++;
+			i++;
+			dataBytes++;
+			dev->checkpointByteCount++;
+		}
+	}
+	
+	return 	i;
+}
+
+int yaffs_CheckpointClose(yaffs_Device *dev)
+{
+
+	if(dev->checkpointOpenForWrite){	
+		if(dev->checkpointByteOffset != 0)
+			yaffs_CheckpointFlushBuffer(dev);
+	}
+
+	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
+			dev->checkpointByteCount));
+			
+	if(dev->checkpointBuffer){
+		/* flush the buffer */	
+		YFREE(dev->checkpointBuffer);
+		dev->checkpointBuffer = NULL;
+		return 1;
+	}
+	else
+		return 0;
+	
+}
+
+int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
+{
+	/* Erase the first checksum block */
+
+	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
+
+	if(!yaffs_CheckpointSpaceOk(dev))
+		return 0;
+
+	if(dev->eraseBlockInNAND){
+		T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt data"TENDSTR)));
+
+		return dev->eraseBlockInNAND(dev, dev->checkpointStartBlock);
+	}
+	else
+		return 0;
+}
+
+
+
new file mode 100644
--- /dev/null
+++ b/yaffs_checkptrw.h
@@ -0,0 +1,18 @@
+#ifndef __YAFFS_CHECKPTRW_H__
+#define __YAFFS_CHECKPTRW_H__
+
+#include "yaffs_guts.h"
+
+int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting);
+
+int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes);
+
+int yaffs_CheckpointRead(yaffs_Device *dev,void *data, int nBytes);
+
+int yaffs_CheckpointClose(yaffs_Device *dev);
+
+int yaffs_CheckpointInvalidateStream(yaffs_Device *dev);
+
+
+#endif
+
--- a/yaffs_fs.c
+++ b/yaffs_fs.c
@@ -31,7 +31,7 @@
  */
 
 const char *yaffs_fs_c_version =
-    "$Id: yaffs_fs.c,v 1.45 2006-04-25 00:41:43 wookey Exp $";
+    "$Id: yaffs_fs.c,v 1.46 2006-05-08 10:13:34 charles Exp $";
 extern const char *yaffs_guts_c_version;
 
 #include <linux/config.h>
@@ -73,7 +73,10 @@ extern const char *yaffs_guts_c_version;
 #include "yportenv.h"
 #include "yaffs_guts.h"
 
-unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS /* | 0xFFFFFFFF */; 
+unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | 
+			   YAFFS_TRACE_BAD_BLOCKS | 
+			   YAFFS_TRACE_CHECKPOINT
+			   /* | 0xFFFFFFFF */; 
 
 #include <linux/mtd/mtd.h>
 #include "yaffs_mtdif.h"
@@ -1284,7 +1287,10 @@ static void yaffs_put_super(struct super
 	if (dev->putSuperFunc) {
 		dev->putSuperFunc(sb);
 	}
+	
+	yaffs_CheckpointSave(dev);
 	yaffs_Deinitialise(dev);
+	
 	yaffs_GrossUnlock(dev);
 
 	/* we assume this is protected by lock_kernel() in mount/umount */
@@ -1484,7 +1490,9 @@ static struct super_block *yaffs_interna
 		dev->nBytesPerChunk = mtd->oobblock;
 		dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
 		nBlocks = mtd->size / mtd->erasesize;
-		dev->startBlock = 0;
+		dev->checkpointStartBlock = 0;
+		dev->checkpointEndBlock = 20;
+		dev->startBlock = dev->checkpointEndBlock + 1;
 		dev->endBlock = nBlocks - 1;
 	} else {
 		dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
--- a/yaffs_guts.c
+++ b/yaffs_guts.c
@@ -13,7 +13,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.31 2006-03-08 07:59:20 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.32 2006-05-08 10:13:34 charles Exp $";
 
 #include "yportenv.h"
 
@@ -22,6 +22,12 @@ const char *yaffs_guts_c_version =
 #include "yaffs_tagsvalidity.h"
 
 #include "yaffs_tagscompat.h"
+#include "yaffs_nand.h"
+
+#include "yaffs_checkptrw.h"
+
+#include "yaffs_nand.h"
+
 
 #ifdef CONFIG_YAFFS_WINCE
 void yfsd_LockYAFFS(BOOL fsLockOnly);
@@ -32,21 +38,7 @@ void yfsd_UnlockYAFFS(BOOL fsLockOnly);
 
 #include "yaffs_ecc.h"
 
-/* NAND access */
-
-static Y_INLINE int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev,
-						    int chunkInNAND,
-						    __u8 * buffer,
-						    yaffs_ExtendedTags * tags);
-static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
-						   int chunkInNAND,
-						   const __u8 * data,
-						   yaffs_ExtendedTags * tags);
-static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo);
-static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device * dev,
-						 int blockNo,
-						 yaffs_BlockState * state,
-						 unsigned *sequenceNumber);
+
 /* Robustification (if it ever comes about...) */
 static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND);
 static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND);
@@ -59,6 +51,8 @@ static void yaffs_HandleUpdateChunk(yaff
 /* Other local prototypes */
 static int yaffs_UnlinkObject( yaffs_Object *obj);
 
+static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList);
+
 static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev,
 					     const __u8 * buffer,
 					     yaffs_ExtendedTags * tags,
@@ -108,110 +102,7 @@ static int yaffs_CheckFileSanity(yaffs_O
 static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in);
 static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId);
 
-
-/*
- *   Start of real code.
- */
-
-/*
- * NAND access layer
- */
- 
-
-
-static int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
-					   __u8 * buffer,
-					   yaffs_ExtendedTags * tags)
-{
-	chunkInNAND -= dev->chunkOffset;
-
-	if (dev->readChunkWithTagsFromNAND)
-		return dev->readChunkWithTagsFromNAND(dev, chunkInNAND, buffer,
-						      tags);
-	else
-		return yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
-									chunkInNAND,
-									buffer,
-									tags);
-}
-
-static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
-						   int chunkInNAND,
-						   const __u8 * buffer,
-						   yaffs_ExtendedTags * tags)
-{
-	chunkInNAND -= dev->chunkOffset;
-
-	if (tags) {
-		tags->sequenceNumber = dev->sequenceNumber;
-		tags->chunkUsed = 1;
-		if (!yaffs_ValidateTags(tags)) {
-			T(YAFFS_TRACE_ERROR,
-			  (TSTR("Writing uninitialised tags" TENDSTR)));
-			YBUG();
-		}
-		T(YAFFS_TRACE_WRITE,
-		  (TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND,
-		   tags->objectId, tags->chunkId));
-	} else {
-		T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR)));
-		YBUG();
-	}
-
-	if (dev->writeChunkWithTagsToNAND)
-		return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer,
-						     tags);
-	else
-		return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev,
-								       chunkInNAND,
-								       buffer,
-								       tags);
-}
-
-static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo)
-{
-	blockNo -= dev->blockOffset;
-
-	if (dev->markNANDBlockBad)
-		return dev->markNANDBlockBad(dev, blockNo);
-	else
-		return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo);
-}
-static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device * dev,
-						 int blockNo,
-						 yaffs_BlockState * state,
-						 unsigned *sequenceNumber)
-{
-	blockNo -= dev->blockOffset;
-
-	if (dev->queryNANDBlock)
-		return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber);
-	else
-		return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo,
-							     state,
-							     sequenceNumber);
-}
-
-static int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
-				  int blockInNAND)
-{
-	int result;
-
-	blockInNAND -= dev->blockOffset;
-
-	dev->nBlockErasures++;
-	result = dev->eraseBlockInNAND(dev, blockInNAND);
-
-	/* If at first we don't succeed, try again *once*.*/
-	if (!result)
-		result = dev->eraseBlockInNAND(dev, blockInNAND);	
-	return result;
-}
-
-static int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
-{
-	return dev->initialiseNAND(dev);
-}
+static void yaffs_InvalidateCheckpoint(yaffs_Device *dev);
 
 /* 
  * Temporary buffer manipulations.
@@ -400,6 +291,8 @@ static int yaffs_WriteNewChunkWithTagsTo
 
 	int writeOk = 1;
 	int attempts = 0;
+	
+	yaffs_InvalidateCheckpoint(dev);
 
 	do {
 		chunk = yaffs_AllocateChunk(dev, useReserve);
@@ -454,6 +347,8 @@ static int yaffs_WriteNewChunkWithTagsTo
 static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND)
 {
 
+	yaffs_InvalidateCheckpoint(dev);
+	
 	yaffs_MarkBlockBad(dev, blockInNAND);
 
 	yaffs_GetBlockInfo(dev, blockInNAND)->blockState =
@@ -620,7 +515,7 @@ static int yaffs_CreateTnodes(yaffs_Devi
 
 /* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */
 
-static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev)
+static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev)
 {
 	yaffs_Tnode *tn = NULL;
 
@@ -640,13 +535,21 @@ static yaffs_Tnode *yaffs_GetTnode(yaffs
 #endif
 		dev->freeTnodes = dev->freeTnodes->internal[0];
 		dev->nFreeTnodes--;
-		/* zero out */
-		memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
 	}
 
 	return tn;
 }
 
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev)
+{
+	yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev);
+	
+	if(tn)
+		memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+
+	return tn;	
+}
+
 /* FreeTnode frees up a tnode and puts it back on the free list */
 static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn)
 {
@@ -813,18 +716,21 @@ static yaffs_Tnode *yaffs_FindLevel0Tnod
  *  2. Scan down the tree towards the level 0 tnode adding tnodes if required.
  *
  * Used when modifying the tree.
+ *
+ *  If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will
+ *  be plugged into the ttree.
  */
  
 static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev,
 					       yaffs_FileStructure * fStruct,
-					       __u32 chunkId)
+					       __u32 chunkId,
+					       yaffs_Tnode *passedTn)
 {
 
-	yaffs_Tnode *tn;
-
 	int requiredTallness;
 	int i;
 	int l;
+	yaffs_Tnode *tn;
 
 	__u32 x;
 
@@ -870,19 +776,42 @@ static yaffs_Tnode *yaffs_AddOrFindLevel
 
 	l = fStruct->topLevel;
 	tn = fStruct->top;
-	while (l > 0 && tn) {
-		x = (chunkId >>
-		     ( YAFFS_TNODES_LEVEL0_BITS +
-		      (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
-		    YAFFS_TNODES_INTERNAL_MASK;
-
-
-		if (!tn->internal[x])
-			tn->internal[x] = yaffs_GetTnode(dev);
-
-		tn = tn->internal[x];
-		l--;
-
+	
+	if(l > 0) {
+		while (l > 0 && tn) {
+			x = (chunkId >>
+			     ( YAFFS_TNODES_LEVEL0_BITS +
+			      (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) &
+			    YAFFS_TNODES_INTERNAL_MASK;
+
+
+			if((l>1) && !tn->internal[x]){
+				/* Add missing non-level-zero tnode */
+				tn->internal[x] = yaffs_GetTnode(dev);
+
+			} else if(l == 1) {
+				/* Looking from level 1 at level 0 */
+			 	if (passedTn) {
+					/* If we already have one, then release it.*/
+					if(tn->internal[x])
+						yaffs_FreeTnode(dev,tn->internal[x]);
+					tn->internal[x] = passedTn;
+			
+				} else if(!tn->internal[x]) {
+					/* Don't have one, none passed in */
+					tn->internal[x] = yaffs_GetTnode(dev);
+				}
+			}
+		
+			tn = tn->internal[x];
+			l--;
+		}
+	} else {
+		/* top is level 0 */
+		if(passedTn) {
+			memcpy(tn,passedTn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+			yaffs_FreeTnode(dev,passedTn);
+		}
 	}
 
 	return tn;
@@ -1569,6 +1498,7 @@ static yaffs_Object *yaffs_FindOrCreateO
 	return theObject;
 
 }
+			
 
 static YCHAR *yaffs_CloneString(const YCHAR * str)
 {
@@ -1819,8 +1749,10 @@ int yaffs_RenameObject(yaffs_Object * ol
 
 /*------------------------- Block Management and Page Allocation ----------------*/
 
-static int yaffs_InitialiseBlocks(yaffs_Device * dev, int nBlocks)
+static int yaffs_InitialiseBlocks(yaffs_Device * dev)
 {
+	int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+	
 	dev->allocationBlock = -1;	/* force it to get a new one */
 
 	/* Todo we're assuming the malloc will pass. */
@@ -1833,7 +1765,7 @@ static int yaffs_InitialiseBlocks(yaffs_
 		dev->blockInfoAlt = 0;
 	
 	/* Set up dynamic blockinfo stuff. */
-	dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8;
+	dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; // round up bytes
 	dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
 	if(!dev->chunkBits){
 		dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks);
@@ -2005,6 +1937,7 @@ static void yaffs_BlockBecameDirty(yaffs
 	bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
 
 	if (!bi->needsRetiring) {
+		yaffs_InvalidateCheckpoint(dev);
 		erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
 		if (!erasedOk) {
 			dev->nErasureFailures++;
@@ -2601,8 +2534,10 @@ static int yaffs_PutChunkIntoFile(yaffs_
 		return YAFFS_OK;
 	}
 
-	tn = yaffs_AddOrFindLevel0Tnode(dev, &in->variant.fileVariant,
-					chunkInInode);
+	tn = yaffs_AddOrFindLevel0Tnode(dev, 
+					&in->variant.fileVariant,
+					chunkInInode,
+					NULL);
 	if (!tn) {
 		return YAFFS_FAIL;
 	}
@@ -3235,6 +3170,433 @@ static void yaffs_InvalidateWholeChunkCa
 	}
 }
 
+/*--------------------- Checkpointing --------------------*/
+
+
+static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head)
+{
+	yaffs_CheckpointValidity cp;
+	cp.structType = sizeof(cp);
+	cp.magic = YAFFS_MAGIC;
+	cp.version = 1;
+	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 == 1) &&
+		     (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->chunkId = obj->chunkId;
+	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 void yaffs_CheckpointObjectToObject( yaffs_Object *obj,yaffs_CheckpointObject *cp)
+{
+
+	yaffs_Object *parent;
+	
+	obj->objectId = cp->objectId;
+	
+	if(cp->parentId)
+		parent = yaffs_FindOrCreateObjectByNumber(
+					obj->myDev,
+					cp->parentId,
+					YAFFS_OBJECT_TYPE_DIRECTORY);
+	else
+		parent = NULL;
+		
+	if(parent)
+		yaffs_AddObjectToDirectory(parent, obj);
+		
+	obj->chunkId = cp->chunkId;
+	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->objectId >= YAFFS_NOBJECT_BUCKETS)
+		obj->lazyLoaded = 1;
+}
+
+
+
+static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn,
+				  	__u32 level, int chunkOffset)
+{
+	int i;
+	yaffs_Device *dev = in->myDev;
+
+	if (tn) {
+		if (level > 0) {
+
+			for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++){
+				if (tn->internal[i]) {
+					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;
+			/* printf("write tnode at %d\n",baseOffset); */
+			yaffs_CheckpointWrite(dev,&baseOffset,sizeof(baseOffset));
+			yaffs_CheckpointWrite(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+		}
+	}
+
+	return 1;
+
+}
+
+static int yaffs_WriteCheckpointTnodes(yaffs_Object *obj)
+{
+	__u32 endMarker = ~0;
+	int ok;
+	
+	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;
+	
+	ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk));
+	
+	while(ok && (~baseChunk)){
+		/* Read level 0 tnode */
+		
+		/* printf("read  tnode at %d\n",baseChunk); */
+		tn = yaffs_GetTnodeRaw(dev);
+		if(tn)
+			ok = (yaffs_CheckpointRead(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8) ==
+			      (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+		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));
+		
+	}
+
+	return ok ? 1 : 0;	
+}
+ 
+
+static int yaffs_WriteCheckpointObjects(yaffs_Device *dev)
+{
+	yaffs_Object *obj;
+	yaffs_CheckpointObject cp;
+	int i;
+	int ok = 1;
+	struct list_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++){
+	 	list_for_each(lh, &dev->objectBucket[i].list) {
+			if (lh) {
+				obj = list_entry(lh, yaffs_Object, hashLink);
+				if (!obj->deferedFree) {
+					yaffs_ObjectToCheckpointObject(&cp,obj);
+					cp.structType = sizeof(cp);
+					/* printf("Write out object %d type %d\n",obj->objectId,obj->variantType); */
+					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)) {
+			/* printf("structure parsing failed\n"); */
+			ok = 0;
+		}
+			
+		if(ok && cp.objectId == ~0)
+			done = 1;
+		else {
+			/* printf("Read object %d type %d\n",cp.objectId,cp.variantType); */
+			obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType);
+			if(obj) {
+				yaffs_CheckpointObjectToObject(obj,&cp);
+				if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) {
+					yaffs_ReadCheckpointTnodes(obj);
+				} else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) {
+					obj->hardLinks.next =
+						    (struct list_head *)
+						    hardList;
+					hardList = obj;
+				}
+			   
+			}
+		}
+	}
+	
+	if(ok)
+		yaffs_HardlinkFixup(dev,hardList);
+	
+	return ok ? 1 : 0;
+}
+
+static int yaffs_WriteCheckpointData(yaffs_Device *dev)
+{
+
+	int ok;
+	
+	ok = yaffs_CheckpointOpen(dev,1);
+	
+	if(ok)
+		ok = yaffs_WriteCheckpointValidityMarker(dev,1);
+	if(ok)
+		ok = yaffs_WriteCheckpointDevice(dev);
+	if(ok)
+		ok = yaffs_WriteCheckpointObjects(dev);
+	if(ok)
+		ok = yaffs_WriteCheckpointValidityMarker(dev,0);
+		
+	yaffs_CheckpointClose(dev);
+		
+	if(ok)
+	    	dev->isCheckpointed = 1;
+	 else 
+	 	dev->isCheckpointed = 0;
+
+	return dev->isCheckpointed;
+}
+
+static int yaffs_ReadCheckpointData(yaffs_Device *dev)
+{
+	int ok;
+	
+	ok = yaffs_CheckpointOpen(dev,0); /* open for read */
+	
+	if(ok)
+		ok = yaffs_ReadCheckpointValidityMarker(dev,1);
+	if(ok)
+		ok = yaffs_ReadCheckpointDevice(dev);
+	if(ok)
+		ok = yaffs_ReadCheckpointObjects(dev);
+	if(ok)
+		ok = yaffs_ReadCheckpointValidityMarker(dev,0);
+		
+		
+	if(ok)
+	    	dev->isCheckpointed = 1;
+	 else 
+	 	dev->isCheckpointed = 0;
+
+	yaffs_CheckpointClose(dev);
+
+	return ok ? 1 : 0;
+
+}
+
+static void yaffs_InvalidateCheckpoint(yaffs_Device *dev)
+{
+	if(dev->isCheckpointed){
+		dev->isCheckpointed = 0;
+		yaffs_CheckpointInvalidateStream(dev);
+	}
+}
+
+
+int yaffs_CheckpointSave(yaffs_Device *dev)
+{
+	if(!dev->isCheckpointed)
+		yaffs_WriteCheckpointData(dev);
+	
+	return dev->isCheckpointed;
+}
+
+int yaffs_CheckpointRestore(yaffs_Device *dev)
+{
+	
+	return yaffs_ReadCheckpointData(dev);
+}
+
 /*--------------------- File read/write ------------------------
  * Read and write have very similar structures.
  * In general the read/write has three parts to it
@@ -3947,6 +4309,41 @@ typedef struct {
 	int block;
 } yaffs_BlockIndex;
 
+
+static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList)
+{
+	yaffs_Object *hl;
+	yaffs_Object *in;
+	
+	while (hardList) {
+		hl = hardList;
+		hardList = (yaffs_Object *) (hardList->hardLinks.next);
+
+		in = yaffs_FindObjectByNumber(dev,
+					      hl->variant.hardLinkVariant.
+					      equivalentObjectId);
+
+		if (in) {
+			/* Add the hardlink pointers */
+			hl->variant.hardLinkVariant.equivalentObject = in;
+			list_add(&hl->hardLinks, &in->hardLinks);
+		} else {
+			/* Todo Need to report/handle this better.
+			 * Got a problem... hardlink to a non-existant object
+			 */
+			hl->variant.hardLinkVariant.equivalentObject = NULL;
+			INIT_LIST_HEAD(&hl->hardLinks);
+
+		}
+
+	}
+
+}
+
+
+
+
+
 static int yaffs_Scan(yaffs_Device * dev)
 {
 	yaffs_ExtendedTags tags;
@@ -4394,33 +4791,14 @@ static int yaffs_Scan(yaffs_Device * dev
 		YFREE(blockIndex);
 	}
 	
+	
 	/* Ok, we've done all the scanning.
 	 * Fix up the hard link chains.
 	 * We should now have scanned all the objects, now it's time to add these 
 	 * hardlinks.
 	 */
-	while (hardList) {
-		hl = hardList;
-		hardList = (yaffs_Object *) (hardList->hardLinks.next);
-
-		in = yaffs_FindObjectByNumber(dev,
-					      hl->variant.hardLinkVariant.
-					      equivalentObjectId);
-
-		if (in) {
-			/* Add the hardlink pointers */
-			hl->variant.hardLinkVariant.equivalentObject = in;
-			list_add(&hl->hardLinks, &in->hardLinks);
-		} else {
-			/* Todo Need to report/handle this better.
-			 * Got a problem... hardlink to a non-existant object
-			 */
-			hl->variant.hardLinkVariant.equivalentObject = NULL;
-			INIT_LIST_HEAD(&hl->hardLinks);
-
-		}
-
-	}
+
+	yaffs_HardlinkFixup(dev,hardList);
 
 	/* Handle the unlinked files. Since they were left in an unlinked state we should
 	 * just delete them.
@@ -4502,7 +4880,6 @@ static int yaffs_ScanBackwards(yaffs_Dev
 	int deleted;
 	yaffs_BlockState state;
 	yaffs_Object *hardList = NULL;
-	yaffs_Object *hl;
 	yaffs_BlockInfo *bi;
 	int sequenceNumber;
 	yaffs_ObjectHeader *oh;
@@ -5040,29 +5417,12 @@ static int yaffs_ScanBackwards(yaffs_Dev
 	 * We should now have scanned all the objects, now it's time to add these 
 	 * hardlinks.
 	 */
-	while (hardList) {
-		hl = hardList;
-		hardList = (yaffs_Object *) (hardList->hardLinks.next);
-
-		in = yaffs_FindObjectByNumber(dev,
-					      hl->variant.hardLinkVariant.
-					      equivalentObjectId);
-
-		if (in) {
-			/* Add the hardlink pointers */
-			hl->variant.hardLinkVariant.equivalentObject = in;
-			list_add(&hl->hardLinks, &in->hardLinks);
-		} else {
-			/* Todo Need to report/handle this better.
-			 * Got a problem... hardlink to a non-existant object
-			 */
-			hl->variant.hardLinkVariant.equivalentObject = NULL;
-			INIT_LIST_HEAD(&hl->hardLinks);
-
-		}
-
-	}
-
+	yaffs_HardlinkFixup(dev,hardList);
+	
+	
+	/*
+	*  Sort out state of unlinked and deleted objects.
+	*/
 	{
 		struct list_head *i;
 		struct list_head *n;
@@ -5490,12 +5850,33 @@ static int yaffs_CheckDevFunctions(const
 	return 0;		/* bad */
 }
 
+
+static void yaffs_CreateInitialDirectories(yaffs_Device *dev)
+{
+	/* Initialise the unlinked, deleted, root and lost and found directories */
+	
+	dev->lostNFoundDir = dev->rootDir =  NULL;
+	dev->unlinkedDir = dev->deletedDir = NULL;
+
+	dev->unlinkedDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);
+	dev->deletedDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);
+
+	dev->rootDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT,
+				      YAFFS_ROOT_MODE | S_IFDIR);
+	dev->lostNFoundDir =
+	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND,
+				      YAFFS_LOSTNFOUND_MODE | S_IFDIR);
+	yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir);
+}
+
 int yaffs_GutsInitialise(yaffs_Device * dev)
 {
 	unsigned x;
 	int bits;
 	int extraBits;
-	int nBlocks;
 
 	T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR)));
 
@@ -5521,7 +5902,13 @@ int yaffs_GutsInitialise(yaffs_Device * 
 
 	/* Check geometry parameters. */
 
-	if ((dev->isYaffs2 && dev->nBytesPerChunk < 1024) || (!dev->isYaffs2 && dev->nBytesPerChunk != 512) || dev->nChunksPerBlock < 2 || dev->nReservedBlocks < 2 || dev->internalStartBlock <= 0 || dev->internalEndBlock <= 0 || dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2)	// otherwise it is too small
+	if ((dev->isYaffs2 && dev->nBytesPerChunk < 1024) || 
+	    (!dev->isYaffs2 && dev->nBytesPerChunk != 512) || 
+	     dev->nChunksPerBlock < 2 || 
+	     dev->nReservedBlocks < 2 || 
+	     dev->internalStartBlock <= 0 || 
+	     dev->internalEndBlock <= 0 || 
+	     dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2)	// otherwise it is too small
 	    ) {
 		T(YAFFS_TRACE_ALWAYS,
 		  (TSTR
@@ -5563,7 +5950,7 @@ int yaffs_GutsInitialise(yaffs_Device * 
 
 	dev->isMounted = 1;
 
-	nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+
 
 	/* OK now calculate a few things for the device
 	 * Calculate chunkGroupBits.
@@ -5640,7 +6027,7 @@ int yaffs_GutsInitialise(yaffs_Device * 
 	dev->nErasedBlocks = 0;
 	dev->isDoingGC = 0;
 
-	/* Initialise temporary buffers */
+	/* Initialise temporary buffers and caches. */
 	{
 		int i;
 		for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
@@ -5649,15 +6036,7 @@ int yaffs_GutsInitialise(yaffs_Device * 
 			    YMALLOC(dev->nBytesPerChunk);
 		}
 	}
-
-	yaffs_InitialiseBlocks(dev, nBlocks);
-
-	yaffs_InitialiseTnodes(dev);
-
-	yaffs_InitialiseObjects(dev);
-
-	dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32));
-
+	
 	if (dev->nShortOpCaches > 0) {
 		int i;
 
@@ -5678,32 +6057,41 @@ int yaffs_GutsInitialise(yaffs_Device * 
 	}
 
 	dev->cacheHits = 0;
-
-	/* Initialise the unlinked, root and lost and found directories */
-	dev->lostNFoundDir = dev->rootDir =  NULL;
-	dev->unlinkedDir = dev->deletedDir = NULL;
-
-	dev->unlinkedDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR);
-	dev->deletedDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR);
-
-	dev->rootDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT,
-				      YAFFS_ROOT_MODE | S_IFDIR);
-	dev->lostNFoundDir =
-	    yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND,
-				      YAFFS_LOSTNFOUND_MODE | S_IFDIR);
-	yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir);
+	
+	dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32));
 
 	if (dev->isYaffs2) {
 		dev->useHeaderFileSize = 1;
 	}
 
-	/* Now scan the flash.   */
-	if (dev->isYaffs2)
-		yaffs_ScanBackwards(dev);
-	else
+	yaffs_InitialiseBlocks(dev);
+	yaffs_InitialiseTnodes(dev);
+	yaffs_InitialiseObjects(dev);
+
+	yaffs_CreateInitialDirectories(dev);
+
+
+	/* Now scan the flash. */
+	if (dev->isYaffs2) {
+		if(yaffs_CheckpointRestore(dev)) {
+			T(YAFFS_TRACE_ALWAYS,
+			  (TSTR("yaffs: restored from checkpoint" TENDSTR)));
+		} else {
+
+			/* Clean up the mess caused by an aborted checkpoint load 
+			 * and scan backwards. 
+			 */
+			yaffs_DeinitialiseBlocks(dev);
+			yaffs_DeinitialiseTnodes(dev);
+			yaffs_DeinitialiseObjects(dev);
+			yaffs_InitialiseBlocks(dev);
+			yaffs_InitialiseTnodes(dev);
+			yaffs_InitialiseObjects(dev);
+			yaffs_CreateInitialDirectories(dev);
+
+			yaffs_ScanBackwards(dev);
+		}
+	}else
 		yaffs_Scan(dev);
 
 	/* Zero out stats */
--- a/yaffs_guts.h
+++ b/yaffs_guts.h
@@ -14,7 +14,7 @@
  *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
  *
- * $Id: yaffs_guts.h,v 1.20 2006-03-08 07:59:20 charles Exp $
+ * $Id: yaffs_guts.h,v 1.21 2006-05-08 10:13:34 charles Exp $
  */
 
 #ifndef __YAFFS_GUTS_H__
@@ -64,8 +64,11 @@
 
 #define YAFFS_NOBJECT_BUCKETS		256
 
+
 #define YAFFS_OBJECT_SPACE		0x40000
 
+#define YAFFS_NCHECKPOINT_OBJECTS	5000
+
 #ifdef CONFIG_YAFFS_UNICODE
 #define YAFFS_MAX_NAME_LENGTH		127
 #define YAFFS_MAX_ALIAS_LENGTH		79
@@ -76,12 +79,18 @@
 
 #define YAFFS_SHORT_NAME_LENGTH		15
 
-/* Some special object ids */
+/* Some special object ids for pseudo objects */
 #define YAFFS_OBJECTID_ROOT		1
 #define YAFFS_OBJECTID_LOSTNFOUND	2
 #define YAFFS_OBJECTID_UNLINKED		3
 #define YAFFS_OBJECTID_DELETED		4
 
+/* Sseudo object ids for checkpointing */
+#define YAFFS_OBJECTID_SB_HEADER	0x10
+#define YAFFS_OBJECTID_CHECKPOINT_DATA	0x20
+
+/* */
+
 #define YAFFS_MAX_SHORT_OP_CACHES	20
 
 #define YAFFS_N_TEMP_BUFFERS		4
@@ -463,6 +472,31 @@ typedef struct {
 	int count;
 } yaffs_ObjectBucket;
 
+
+/* yaffs_CheckpointObject holds the definition of an object as dumped 
+ * by checkpointing.
+ */
+
+typedef struct {
+        int structType;
+	__u32 objectId;		
+	__u32 parentId;
+	int chunkId;
+			
+	yaffs_ObjectType variantType:3;
+	__u8 deleted:1;		
+	__u8 softDeleted:1;	
+	__u8 unlinked:1;	
+	__u8 fake:1;		
+	__u8 renameAllowed:1;
+	__u8 unlinkAllowed:1;
+	__u8 serial;		
+	
+	int nDataChunks;	
+	__u32 fileSizeOrEquivalentObjectId;
+
+}yaffs_CheckpointObject;
+
 /*--------------------- Temporary buffers ----------------
  *
  * These are chunk-sized working buffers. Each device has a few
@@ -488,6 +522,13 @@ struct yaffs_DeviceStruct {
 	int endBlock;		/* End block we're allowed to use */
 	int nReservedBlocks;	/* We want this tuneable so that we can reduce */
 				/* reserved blocks on NOR and RAM. */
+	/* Stuff used by checkpointing */
+	int headerBlock;
+	int checkpointStartBlock;
+	int checkpointEndBlock;
+
+	
+
 
 	int nShortOpCaches;	/* If <= 0, then short op caching is disabled, else
 				 * the number of short op caches (don't use too many)
@@ -558,13 +599,24 @@ struct yaffs_DeviceStruct {
 #endif
 
 	int isMounted;
+	
+	int isCheckpointed;
 
 	/* Stuff to support block offsetting to support start block zero */
 	int internalStartBlock;
 	int internalEndBlock;
 	int blockOffset;
 	int chunkOffset;
+	
 
+	/* Runtime checkpointing stuff */
+	int checkpointBlock;
+	int checkpointPage;
+	int checkpointByteCount;
+	int checkpointByteOffset;
+	__u8 *checkpointBuffer;
+	int checkpointOpenForWrite;
+	
 	/* Block Info */
 	yaffs_BlockInfo *blockInfo;
 	__u8 *chunkBits;	/* bitmap of chunks in use */
@@ -657,6 +709,45 @@ struct yaffs_DeviceStruct {
 
 typedef struct yaffs_DeviceStruct yaffs_Device;
 
+/* The static layout of bllock usage etc is stored in the super block header */
+typedef struct {
+        int StructType;
+	int version;
+	int checkpointStartBlock;
+	int checkpointEndBlock;
+	int startBlock;
+	int endBlock;
+	int rfu[100];
+} yaffs_SuperBlockHeader;
+	
+/* The CheckpointDevice structure holds the device information that changes at runtime and
+ * must be preserved over unmount/mount cycles.
+ */
+typedef struct {
+        int structType;
+	int nErasedBlocks;
+	int allocationBlock;	/* Current block being allocated off */
+	__u32 allocationPage;
+	int nFreeChunks;
+
+	int nDeletedFiles;		/* Count of files awaiting deletion;*/
+	int nUnlinkedFiles;		/* Count of unlinked files. */
+	int nBackgroundDeletions;	/* Count of background deletions. */
+
+	/* yaffs2 runtime stuff */
+	unsigned sequenceNumber;	/* Sequence number of currently allocating block */
+	unsigned oldestDirtySequence;
+
+} yaffs_CheckpointDevice;
+
+
+typedef struct {
+    int structType;
+    __u32 magic;
+    __u32 version;
+    __u32 head;
+} yaffs_CheckpointValidity;
+
 /* Function to manipulate block info */
 static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk)
 {
@@ -675,8 +766,6 @@ static Y_INLINE yaffs_BlockInfo *yaffs_G
 int yaffs_GutsInitialise(yaffs_Device * dev);
 void yaffs_Deinitialise(yaffs_Device * dev);
 
-void yaffs_FlushEntireDeviceCache(yaffs_Device *dev);
-
 int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev);
 
 int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName,
@@ -705,6 +794,12 @@ yaffs_Object *yaffs_MknodFile(yaffs_Obje
 			      __u32 mode, __u32 uid, __u32 gid);
 int yaffs_FlushFile(yaffs_Object * obj, int updateTime);
 
+/* Flushing and checkpointing */
+void yaffs_FlushEntireDeviceCache(yaffs_Device *dev);
+
+int yaffs_CheckpointSave(yaffs_Device *dev);
+int yaffs_CheckpointRestore(yaffs_Device *dev);
+
 /* Directory operations */
 yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name,
 				   __u32 mode, __u32 uid, __u32 gid);
new file mode 100644
--- /dev/null
+++ b/yaffs_nand.c
@@ -0,0 +1,123 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+ 
+const char *yaffs_nand_c_version =
+    "$Id: yaffs_nand.c,v 1.1 2006-05-08 10:13:34 charles Exp $";
+
+#include "yaffs_nand.h"
+#include "yaffs_tagscompat.h"
+#include "yaffs_tagsvalidity.h"
+
+
+int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
+					   __u8 * buffer,
+					   yaffs_ExtendedTags * tags)
+{
+	chunkInNAND -= dev->chunkOffset;
+
+	if (dev->readChunkWithTagsFromNAND)
+		return dev->readChunkWithTagsFromNAND(dev, chunkInNAND, buffer,
+						      tags);
+	else
+		return yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
+									chunkInNAND,
+									buffer,
+									tags);
+}
+
+int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
+						   int chunkInNAND,
+						   const __u8 * buffer,
+						   yaffs_ExtendedTags * tags)
+{
+	chunkInNAND -= dev->chunkOffset;
+
+	
+	if (tags) {
+		tags->sequenceNumber = dev->sequenceNumber;
+		tags->chunkUsed = 1;
+		if (!yaffs_ValidateTags(tags)) {
+			T(YAFFS_TRACE_ERROR,
+			  (TSTR("Writing uninitialised tags" TENDSTR)));
+			YBUG();
+		}
+		T(YAFFS_TRACE_WRITE,
+		  (TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND,
+		   tags->objectId, tags->chunkId));
+	} else {
+		T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR)));
+		YBUG();
+	}
+
+	if (dev->writeChunkWithTagsToNAND)
+		return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer,
+						     tags);
+	else
+		return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev,
+								       chunkInNAND,
+								       buffer,
+								       tags);
+}
+
+int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo)
+{
+	blockNo -= dev->blockOffset;
+
+;
+	if (dev->markNANDBlockBad)
+		return dev->markNANDBlockBad(dev, blockNo);
+	else
+		return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo);
+}
+
+int yaffs_QueryInitialBlockState(yaffs_Device * dev,
+						 int blockNo,
+						 yaffs_BlockState * state,
+						 unsigned *sequenceNumber)
+{
+	blockNo -= dev->blockOffset;
+
+	if (dev->queryNANDBlock)
+		return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber);
+	else
+		return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo,
+							     state,
+							     sequenceNumber);
+}
+
+
+int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
+				  int blockInNAND)
+{
+	int result;
+
+	blockInNAND -= dev->blockOffset;
+
+
+	dev->nBlockErasures++;
+	result = dev->eraseBlockInNAND(dev, blockInNAND);
+
+	/* If at first we don't succeed, try again *once*.*/
+	if (!result)
+		result = dev->eraseBlockInNAND(dev, blockInNAND);	
+	return result;
+}
+
+int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
+{
+	return dev->initialiseNAND(dev);
+}
+
+
+ 
new file mode 100644
--- /dev/null
+++ b/yaffs_nand.h
@@ -0,0 +1,43 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __YAFFS_NAND_H__
+#define __YAFFS_NAND_H__
+#include "yaffs_guts.h"
+
+
+
+int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
+					   __u8 * buffer,
+					   yaffs_ExtendedTags * tags);
+
+int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
+						   int chunkInNAND,
+						   const __u8 * buffer,
+						   yaffs_ExtendedTags * tags);
+
+int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo);
+
+int yaffs_QueryInitialBlockState(yaffs_Device * dev,
+						 int blockNo,
+						 yaffs_BlockState * state,
+						 unsigned *sequenceNumber);
+
+int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
+				  int blockInNAND);
+
+int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev);
+
+#endif
+
--- a/yportenv.h
+++ b/yportenv.h
@@ -15,7 +15,7 @@
  *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
  *
- * $Id: yportenv.h,v 1.9 2005-10-07 02:46:50 charles Exp $
+ * $Id: yportenv.h,v 1.10 2006-05-08 10:13:34 charles Exp $
  *
  */
 
@@ -148,6 +148,7 @@ extern unsigned yaffs_traceMask;
 #define YAFFS_TRACE_GC_DETAIL		0x00001000
 #define YAFFS_TRACE_SCAN_DEBUG		0x00002000
 #define YAFFS_TRACE_MTD			0x00004000
+#define YAFFS_TRACE_CHECKPOINT		0x00008000
 #define YAFFS_TRACE_ALWAYS		0x40000000
 #define YAFFS_TRACE_BUG			0x80000000