## comment out USE_xxxx if you don't want these features.
KERNELDIR = /usr/src/kernel-headers-2.4.18
+
+# Configurations...
+# Comment out the stuff you don't want.
+#
+
+# CONFIG_YAFFS_RAM_ENABLED.
+# This adds the yaffsram file system support. Nice for testing on x86, but uses 2MB of RAM.
+# Don't enable for NAND-based targets.
+
USE_RAM_FOR_TEST = -DCONFIG_YAFFS_RAM_ENABLED
+
+
+# CONFIG_YAFFS_MTD_ENABLED.
+# This adds the yaffs file system support for working with a NAND mtd.
+
USE_MTD = -DCONFIG_YAFFS_MTD_ENABLED
-YAFFS_CONFIGS = -DCONFIG_YAFFS_USE_GENERIC_RW
-CFLAGS = -D__KERNEL__ -DMODULE $(USE_RAM_FOR_TEST) $(USE_MTD) $(YAFFS_CONFIGS) -I$(KERNELDIR)/include -O2 -Wall
+# CONFIG_YAFFS_USE_GENERIC_RW
+# Use generic_read/generic_write for reading/writing files. This enables the use of the Linux
+# file caching layer.
+#
+# If you disable this, then caching is disabled and file read/write is direct.
+
+USE_GENERIC_RW = -DCONFIG_YAFFS_USE_GENERIC_RW
+
+# CONFIG_YAFFS_USE_HEADER_FILE_SIZE
+# When the flash is scanned, two file sizes are constructed:
+# * The size taken from the object header for the file.
+# * The size figured out by scanning the data chunks.
+# If this option is enabled, then the object header size is ued, otherwise the scanned size is used.
+# Suggest leaving this disabled.
+
+#USE_HEADER_FILE_SIZE = -DCONFIG_YAFFS_USE_HEADER_FILE_SIZE
+
+#CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
+# Enabling this turns off the test that chunks are erased in flash before writing to them.
+# this is safe, since the write verification will fail.
+# Suggest enabling the test (ie. keep the following line commented) during development to help debug things.
+
+#IGNORE_CHUNK_ERASED = -DCONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
+
+#CONFIG_YAFFS_DISABLE_WRITE_VERIFY
+# I am severely reluctant to provide this config. Disabling the verification is not a good thing to do
+# since NAND writes can fail silently.
+# Disabling the write verification will cause your teeth to rot, rats to eat your corn and give you split ends.
+# You have been warned. ie. Don't uncomment the following line.
+
+#IGNORE_WRITE_VERIFY = -DCONFIG_YAFFS_DISBLE_WRITE_VERIFY
+
+# End of configuration options.
+
+YAFFS_CONFIGS = $(USE_RAM_FOR_TEST) $(USE_MTD) $(USE_GENERIC_RW) $(USE_HEADER_FILE_SIZE) $(IGNORE_CHUNK_ERASED) $(IGNORE_WRITE_VERIFY)
+
+CFLAGS = -D__KERNEL__ -DMODULE $(YAFFS_CONFIGS) -I$(KERNELDIR)/include -O2 -Wall
OBJS = yaffs_fs.o yaffs_guts.o yaffs_ramem.o yaffs_mtdif.o nand_ecc.o
all: yaffs.o
-$(OBJS): %.o: %.c
+$(OBJS): %.o: %.c Makefile
gcc -c $(CFLAGS) $< -o $@
yaffs.o: $(OBJS)
\r
static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);\r
static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);\r
-static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name);\r
+static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force);\r
static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId);\r
static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);\r
static int yaffs_CheckStructures(void);\r
\r
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);\r
\r
+static int yaffs_UnlinkWorker(yaffs_Object *obj);\r
\r
\r
static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1);\r
\r
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)\r
{\r
-#if 1\r
\r
static int init = 0;\r
static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];\r
if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return YAFFS_FAIL;\r
if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL;\r
\r
-#endif\r
\r
return YAFFS_OK;\r
\r
{\r
\r
// First check this chunk is erased...\r
+#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK\r
writeOk = yaffs_CheckChunkErased(dev,chunk);\r
- \r
- if(writeOk)\r
+#endif \r
+ if(!writeOk)\r
+ {\r
+ T((TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));\r
+ }\r
+ else\r
{\r
writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare);\r
}\r
// NB We check a raw read without ECC correction applied\r
yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0);\r
\r
+#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY\r
if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare))\r
{\r
// Didn't verify\r
- T((TSTR("**>> yaffs write failed on chunk %d" TENDSTR), chunk));\r
- // yaffs_DeleteChunk(dev,chunk);\r
+ T((TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));\r
\r
writeOk = 0;\r
- } \r
+ } \r
+#endif \r
\r
}\r
if(writeOk)\r
// Set current write block to the new block\r
\r
dev->doingBufferedBlockRewrite = 0;\r
+ \r
+ return 1;\r
}\r
\r
\r
static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)\r
{\r
- \r
+ int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;\r
+\r
+ // Mark the block for retirement\r
+ dev->blockInfo[blockInNAND].needsRetiring = 1;\r
+\r
+ //TODO \r
// Just do a garbage collection on the affected block then retire the block\r
// NB recursion\r
}\r
\r
static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)\r
{\r
+ int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;\r
+\r
+ // Mark the block for retirement\r
+ dev->blockInfo[blockInNAND].needsRetiring = 1;\r
+ // Delete the chunk\r
+ yaffs_DeleteChunk(dev,chunkInNAND);\r
}\r
\r
\r
static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1)\r
{\r
\r
+\r
if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 ||\r
s0->tagByte0 != s1->tagByte0 ||\r
s0->tagByte1 != s1->tagByte1 ||\r
break;\r
}\r
\r
- if(yaffs_UpdateObjectHeader(in,name) < 0)\r
+ if(yaffs_UpdateObjectHeader(in,name,0) < 0)\r
{\r
// Could not create the object header, fail the creation\r
yaffs_UnlinkWorker(in);\r
obj->dirty = 1;\r
yaffs_AddObjectToDirectory(newDir,obj);\r
\r
- if(yaffs_UpdateObjectHeader(obj,newName) >= 0)\r
+ if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0)\r
{\r
return YAFFS_OK;\r
}\r
}\r
\r
\r
-static int yaffs_FindBlockForAllocation(yaffs_Device *dev,int useReserve)\r
+static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)\r
+{\r
+ yaffs_BlockInfo *bi = &dev->blockInfo[blockNo];\r
+ \r
+ int erasedOk = 0;\r
+ \r
+ // If the block is still healthy erase it and mark as clean.\r
+ // If the block has had a data failure, then retire it.\r
+ bi->blockState = YAFFS_BLOCK_STATE_DIRTY;\r
+\r
+ if(!bi->needsRetiring)\r
+ {\r
+ erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);\r
+ if(!erasedOk)\r
+ {\r
+ T((TSTR("**>> Erasure failed %d" TENDSTR),blockNo));\r
+ }\r
+ }\r
+ \r
+ if( erasedOk )\r
+ {\r
+ bi->blockState = YAFFS_BLOCK_STATE_EMPTY;\r
+ dev->nErasedBlocks++;\r
+ bi->pagesInUse = 0;\r
+ bi->pageBits = 0;\r
+ \r
+ T((TSTR("Erased block %d" TENDSTR),blockNo));\r
+ }\r
+ else\r
+ {\r
+ yaffs_RetireBlock(dev,blockNo);\r
+ T((TSTR("**>> Block %d retired" TENDSTR),blockNo));\r
+ }\r
+}\r
+\r
+\r
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev)\r
{\r
int i;\r
\r
- if(useReserve && dev->nErasedBlocks < 1)\r
+ if(dev->nErasedBlocks < 1)\r
{\r
// Hoosterman we've got a problem.\r
// Can't get space to gc\r
return -1;\r
}\r
- else if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)\r
- {\r
- // We are not in GC, so we hold some in reserve so we can get\r
- // a gc done.\r
- }\r
\r
// Find an empty block.\r
\r
}\r
\r
\r
-static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)\r
-{\r
- yaffs_BlockInfo *bi = &dev->blockInfo[blockNo];\r
- \r
- // Mark as dirty.\r
- // If the block is still healthy erase it and mark as clean.\r
- // If the block has had a data failure, then retire it.\r
- bi->blockState = YAFFS_BLOCK_STATE_DIRTY;\r
- \r
- if(!bi->needsRetiring && yaffs_EraseBlockInNAND(dev,blockNo))\r
- {\r
- bi->blockState = YAFFS_BLOCK_STATE_EMPTY;\r
- dev->nErasedBlocks++;\r
- bi->pagesInUse = 0;\r
- bi->pageBits = 0;\r
- \r
- T((TSTR("Erased block %d" TENDSTR),blockNo));\r
- }\r
- else\r
- {\r
- yaffs_RetireBlock(dev,blockNo);\r
- T((TSTR("**>> Block %d retired" TENDSTR),blockNo));\r
- }\r
-}\r
-\r
\r
static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)\r
{\r
if(dev->allocationBlock < 0)\r
{\r
// Get next block to allocate off\r
- dev->allocationBlock = yaffs_FindBlockForAllocation(dev,useReserve);\r
+ dev->allocationBlock = yaffs_FindBlockForAllocation(dev);\r
dev->allocationPage = 0;\r
}\r
\r
+ if(!useReserve && dev->nErasedBlocks <= YAFFS_RESERVED_BLOCKS)\r
+ {\r
+ // Not enough space to allocate unless we're allowed to use the reserve.\r
+ return -1;\r
+ }\r
+ \r
// Next page please....\r
if(dev->allocationBlock >= 0)\r
{\r
dev->allocationBlock = -1;\r
}\r
\r
-#ifdef YAFFS_PARANOID\r
- if(yaffs_CheckChunkErased(dev,retVal) == YAFFS_FAIL)\r
- {\r
- T((TSTR("..................Trying to allocate non-erased page %d" TENDSTR),retVal));\r
- }\r
-#endif \r
+\r
return retVal;\r
\r
}\r
// UpdateObjectHeader updates the header on NAND for an object.\r
// If name is not NULL, then that new name is used.\r
//\r
-int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name)\r
+int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)\r
{\r
\r
yaffs_Device *dev = in->myDev;\r
yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;\r
yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;\r
\r
- if(!in->fake)\r
+ if(!in->fake || force)\r
{\r
\r
yaffs_CheckGarbageCollection(dev); \r
oh->st_ctime = in->st_ctime;\r
oh->st_rdev = in->st_rdev;\r
\r
- oh->parentObjectId = in->parent->objectId;\r
+ if(in->parent)\r
+ {\r
+ oh->parentObjectId = in->parent->objectId;\r
+ }\r
+ else\r
+ {\r
+ oh->parentObjectId = 0;\r
+ }\r
+ \r
oh->sum = in->sum;\r
if(name && *name)\r
{\r
memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);\r
strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);\r
}\r
- else\r
+ else if(prevChunkId)\r
{ \r
memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);\r
}\r
+ else\r
+ {\r
+ memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1); \r
+ }\r
\r
switch(in->variantType)\r
{\r
\r
in->st_mtime = CURRENT_TIME;\r
\r
- retVal = yaffs_UpdateObjectHeader(in,NULL);\r
+ retVal = yaffs_UpdateObjectHeader(in,NULL,0);\r
}\r
else\r
{\r
yaffs_HashObject(hl);\r
\r
// Update the hardlink which has become an object\r
- yaffs_UpdateObjectHeader(hl,NULL);\r
+ yaffs_UpdateObjectHeader(hl,NULL,0);\r
\r
// Finally throw away the deleted object\r
yaffs_DeleteChunk(obj->myDev,obj->chunkId);\r
if(in->variant.fileVariant.scannedFileSize <endpos)\r
{\r
in->variant.fileVariant.scannedFileSize = endpos;\r
+#ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE\r
+ in->variant.fileVariant.fileSize = \r
+ in->variant.fileVariant.scannedFileSize;\r
+#endif\r
+\r
}\r
//T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); \r
}\r
}\r
}\r
\r
- if(!in->valid)\r
+ if(!in->valid &&\r
+ (tags.objectId == YAFFS_OBJECTID_ROOT ||\r
+ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))\r
+ {\r
+ // We only load some info, don't fiddle with directory structure\r
+ in->valid = 1;\r
+ in->variantType = oh->type;\r
+ \r
+ in->st_mode = oh->st_mode;\r
+ in->st_uid = oh->st_uid;\r
+ in->st_gid = oh->st_gid;\r
+ in->st_atime = oh->st_atime;\r
+ in->st_mtime = oh->st_mtime;\r
+ in->st_ctime = oh->st_ctime;\r
+ in->st_rdev = oh->st_rdev;\r
+ in->chunkId = chunk;\r
+\r
+ }\r
+ else if(!in->valid)\r
{\r
// we need to load this info\r
\r
case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem\r
break;\r
case YAFFS_OBJECT_TYPE_FILE:\r
+#ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE\r
in->variant.fileVariant.fileSize = oh->fileSize;\r
+#endif\r
break;\r
case YAFFS_OBJECT_TYPE_HARDLINK:\r
in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;\r
\r
if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);\r
\r
- yaffs_UpdateObjectHeader(obj,NULL);\r
+ yaffs_UpdateObjectHeader(obj,NULL,1);\r
\r
return YAFFS_OK;\r
\r