*/
//yaffs_guts.c
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.28 2003-08-20 03:53:39 charles Exp $";
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.42 2005-06-25 03:22:23 charles Exp $";
#include "yportenv.h"
static __inline __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
{
- if(blk < dev->startBlock || blk > dev->endBlock)
+ if(blk < dev->internalStartBlock || blk > dev->internalEndBlock)
{
T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),blk));
YBUG();
}
- return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->startBlock));
+ return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->internalStartBlock));
}
static __inline__ void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
// Function to manipulate block info
static __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)
{
- if(blk < dev->startBlock || blk > dev->endBlock)
+ if(blk < dev->internalStartBlock || blk > dev->internalEndBlock)
{
T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),blk));
YBUG();
}
- return &dev->blockInfo[blk - dev->startBlock];
+ return &dev->blockInfo[blk - dev->internalStartBlock];
}
static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
{
- if(chunkInNAND < dev->startBlock * dev->nChunksPerBlock)
+ if(chunkInNAND < dev->internalStartBlock * dev->nChunksPerBlock)
{
T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
return YAFFS_FAIL;
}
dev->nPageWrites++;
- return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
+ return dev->writeChunkToNAND(dev,chunkInNAND - dev->chunkOffset,data,spare);
}
if(!dev->useNANDECC)
{
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND - dev->chunkOffset,data,spare);
if(data && doErrorCorrection)
{
// Do ECC correction
if(eccResult1>0)
{
- T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
dev->eccFixed++;
}
else if(eccResult1<0)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
dev->eccUnfixed++;
}
if(eccResult2>0)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
dev->eccFixed++;
}
else if(eccResult2<0)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
dev->eccUnfixed++;
}
{
// Must allocate enough memory for spare+2*sizeof(int) for ecc results from device.
struct yaffs_NANDSpare nspare;
- retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare*)&nspare);
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND - dev->chunkOffset,data,(yaffs_Spare*)&nspare);
memcpy (spare, &nspare, sizeof(yaffs_Spare));
if(data && doErrorCorrection)
{
if(nspare.eccres1>0)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
}
else if(nspare.eccres1<0)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND - dev->chunkOffset));
}
if(nspare.eccres2>0)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
}
else if(nspare.eccres2<0)
{
- T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+ T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND - dev->chunkOffset));
}
if(nspare.eccres1 || nspare.eccres2)
// Might as well always allocate the larger size for dev->useNANDECC == true;
static __u8 spare[sizeof(struct yaffs_NANDSpare)];
- dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
+ dev->readChunkFromNAND(dev,chunkInNAND - dev->chunkOffset,data,(yaffs_Spare *)spare);
if(!init)
{
int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
{
dev->nBlockErasures++;
- return dev->eraseBlockInNAND(dev,blockInNAND);
+ return dev->eraseBlockInNAND(dev,blockInNAND - dev->blockOffset);
}
int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
if(limit)
{
*limit = *limit-1;
- if(limit <= 0)
+ if(*limit <= 0)
{
hitLimit = 1;
}
{
if(tn->level0[i])
{
+ // Note this does not find the real chunk, only the chunk group.
+ // We make an assumption that a chunk group is niot larger than a block.
theChunk = (tn->level0[i] << in->myDev->chunkGroupBits);
T(YAFFS_TRACE_SCAN,(TSTR("soft delete tch %d cgb %d chunk %d" TENDSTR),
tn->level0[i],in->myDev->chunkGroupBits,theChunk));
yaffs_Device *dev = tn->myDev;
+#ifdef __KERNEL__
+ if(tn->myInode)
+ {
+ // We're still hooked up to a cached inode.
+ // Don't delete now, but mark for later deletion
+ tn->deferedFree = 1;
+ return;
+ }
+#endif
+
yaffs_UnhashObject(tn);
// Link into the free list.
}
+#ifdef __KERNEL__
+
+void yaffs_HandleDeferedFree(yaffs_Object *obj)
+{
+ if(obj->deferedFree)
+ {
+ yaffs_FreeObject(obj);
+ }
+}
+
+#endif
+
static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
in = list_entry(i, yaffs_Object,hashLink);
if(in->objectId == number)
{
+#ifdef __KERNEL__
+ // Don't tell the VFS about this if it has been marked for freeing
+ if(in->deferedFree)
+ return NULL;
+#endif
return in;
}
}
#else
-#if defined(CONFIG_KERNEL_2_5)
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME.tv_sec;
-#else
- theObject->st_atime = theObject->st_mtime = theObject->st_ctime = CURRENT_TIME;
-#endif
+ theObject->st_atime = theObject->st_mtime = theObject->st_ctime = Y_CURRENT_TIME;
+
#endif
switch(type)
{
#ifdef CONFIG_YAFFS_WINCE
yfsd_WinFileTimeNow(in->win_atime);
in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
- in->win_ctime[1] = in->win_mtime[1] = in->win_atime[0];
+ in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
#else
-#if defined(CONFIG_KERNEL_2_5)
- in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME.tv_sec;
-#else
- in->st_atime = in->st_mtime = in->st_ctime = CURRENT_TIME;
-#endif
+
+ in->st_atime = in->st_mtime = in->st_ctime = Y_CURRENT_TIME;
in->st_rdev = rdev;
in->st_uid = uid;
in->st_gid = gid;
yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
{
- return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev);
+ return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL,parent,name,mode,uid,gid,NULL,NULL,rdev);
}
yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)
pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
if(aggressive)
{
- iterations = dev->endBlock - dev->startBlock + 1;
+ iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
}
else
{
- iterations = dev->endBlock - dev->startBlock + 1;
+ iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
iterations = iterations / 16;
if(iterations > 200)
{
for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
{
b++;
- if ( b < dev->startBlock || b > dev->endBlock)
+ if ( b < dev->internalStartBlock || b > dev->internalEndBlock)
{
- b = dev->startBlock;
+ b = dev->internalStartBlock;
}
- if(b < dev->startBlock || b > dev->endBlock)
+ if(b < dev->internalStartBlock || b > dev->internalEndBlock)
{
T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));
YBUG();
// Find an empty block.
- for(i = dev->startBlock; i <= dev->endBlock; i++)
+ for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
{
dev->allocationBlockFinder++;
- if(dev->allocationBlockFinder <dev->startBlock || dev->allocationBlockFinder> dev->endBlock)
+ if(dev->allocationBlockFinder <dev->internalStartBlock || dev->allocationBlockFinder> dev->internalEndBlock)
{
- dev->allocationBlockFinder = dev->startBlock;
+ dev->allocationBlockFinder = dev->internalStartBlock;
}
bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
{
// It's a header
object->chunkId = newChunk;
+ object->serial = tags.serialNumber;
}
else
{
}
else
{
+ memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
return 0;
}
if(markNAND)
{
yaffs_SpareInitialise(&spare);
-
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+
+ //read data before write, to ensure correct ecc
+ //if we're using MTD verification under Linux
+ yaffs_ReadChunkFromNAND(dev,chunkId,NULL,&spare,0);
+#endif
+
spare.pageStatus = 0; // To mark it as deleted.
int lowest;
int i;
yaffs_ChunkCache *cache;
- int chunkWritten;
+ int chunkWritten = 0;
int nBytes;
int nCaches = obj->myDev->nShortOpCaches;
// using yaffs_DeleteChunk
chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
- if(chunkId < (dev->startBlock * 32) || chunkId >= ((dev->endBlock+1) * 32))
+ if(chunkId < (dev->internalStartBlock * 32) || chunkId >= ((dev->internalEndBlock+1) * 32))
{
//T(("Found daft chunkId %d for %d\n",chunkId,i));
}
int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;
// Got to read and rewrite the last chunk with its new size.
+ // NB Got to zero pad to nuke old data
yaffs_ReadChunkDataFromObject(in,lastChunk,dev->localBuffer);
-
+ memset(dev->localBuffer + sizeOfPartialChunk,0, YAFFS_BYTES_PER_CHUNK - sizeOfPartialChunk);
+
yaffs_WriteChunkDataToObject(in,lastChunk,dev->localBuffer,sizeOfPartialChunk,1);
}
#ifdef CONFIG_YAFFS_WINCE
yfsd_WinFileTimeNow(in->win_mtime);
#else
-#if defined(CONFIG_KERNEL_2_5)
- in->st_mtime = CURRENT_TIME.tv_sec;
-#else
- in->st_mtime = CURRENT_TIME;
-#endif
+ in->st_mtime = Y_CURRENT_TIME;
#endif
}
yaffs_RemoveObjectFromDirectory(in);
yaffs_DeleteChunk(in->myDev,in->chunkId,1);
+ in->chunkId = -1;
+#if 0
#ifdef __KERNEL__
if(in->myInode)
{
in->myInode->u.generic_ip = NULL;
- in->myInode = 0;
+ in->myInode = NULL;
}
+#endif
#endif
yaffs_FreeObject(in);
return YAFFS_OK;
#ifdef __KERNEL__
if(!in->myInode)
{
+ // Might be open at present,
+ // Caught by delete_inode in yaffs_fs.c
immediateDeletion = 1;
}
case YAFFS_OBJECT_TYPE_SYMLINK:
return yaffs_DeleteSymLink(obj);
break;
+ case YAFFS_OBJECT_TYPE_SPECIAL:
+ return yaffs_DoGenericObjectDeletion(obj);
+ break;
case YAFFS_OBJECT_TYPE_HARDLINK:
case YAFFS_OBJECT_TYPE_UNKNOWN:
default:
// Scan all the blocks...
- for(blk = dev->startBlock; blk <= dev->endBlock; blk++)
+ for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
{
deleted = 0;
bi = yaffs_GetBlockInfo(dev,blk);
{
// Hoosterman, another problem....
// We're trying to use a non-directory as a directory
- // Todo ... handle
- T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan" TENDSTR)));
+
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
+ parent = dev->lostNFoundDir;
}
if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;
if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;
-#if defined(CONFIG_KERNEL_2_5)
- if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime.tv_sec;
- if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime.tv_sec;
- if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime.tv_sec;
-#else
- if(valid & ATTR_ATIME) obj->st_atime = attr->ia_atime;
- if(valid & ATTR_CTIME) obj->st_ctime = attr->ia_ctime;
- if(valid & ATTR_MTIME) obj->st_mtime = attr->ia_mtime;
-#endif
+ if(valid & ATTR_ATIME) obj->st_atime = Y_TIME_CONVERT(attr->ia_atime);
+ if(valid & ATTR_CTIME) obj->st_ctime = Y_TIME_CONVERT(attr->ia_ctime);
+ if(valid & ATTR_MTIME) obj->st_mtime = Y_TIME_CONVERT(attr->ia_mtime);
+
if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
return YAFFS_OK;
}
+
int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
{
unsigned int valid = 0;
attr->ia_uid = obj->st_uid; valid |= ATTR_UID;
attr->ia_gid = obj->st_gid; valid |= ATTR_GID;
-#if defined(CONFIG_KERNEL_2_5)
- attr->ia_atime.tv_sec = obj->st_atime; valid |= ATTR_ATIME;
- attr->ia_ctime.tv_sec = obj->st_ctime; valid |= ATTR_CTIME;
- attr->ia_mtime.tv_sec = obj->st_mtime; valid |= ATTR_MTIME;
-#else
- attr->ia_atime = obj->st_atime; valid |= ATTR_ATIME;
- attr->ia_ctime = obj->st_ctime; valid |= ATTR_CTIME;
- attr->ia_mtime = obj->st_mtime; valid |= ATTR_MTIME;
-#endif
+
+ Y_TIME_CONVERT(attr->ia_atime) = obj->st_atime; valid |= ATTR_ATIME;
+ Y_TIME_CONVERT(attr->ia_ctime) = obj->st_ctime; valid |= ATTR_CTIME;
+ Y_TIME_CONVERT(attr->ia_mtime) = obj->st_mtime; valid |= ATTR_MTIME;
+
attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
attr->ia_valid = valid;
int extraBits;
int nBlocks;
- if( dev->nBytesPerChunk != YAFFS_BYTES_PER_CHUNK ||
-
+ if( dev->nBytesPerChunk != YAFFS_BYTES_PER_CHUNK ||
dev->nChunksPerBlock < 2 ||
dev->nReservedBlocks < 2 ||
- dev->startBlock <= 0 ||
+ dev->startBlock < 0 ||
dev->endBlock <= 0 ||
dev->endBlock <= (dev->startBlock + dev->nReservedBlocks)
)
{
//these parameters must be set before stating yaffs
- // Other parameters startBlock,
+ // Other parameters internalStartBlock,
return YAFFS_FAIL;
}
}
dev->isMounted = 1;
-
- if(dev->startBlock <= 0 ||
+
+ if(dev->startBlock < 0 ||
(dev->endBlock - dev->startBlock) < 10)
{
T(YAFFS_TRACE_ALWAYS,(TSTR("startBlock %d or endBlock %d invalid\n" TENDSTR),
return YAFFS_FAIL;
}
- nBlocks = dev->endBlock - dev->startBlock + 1;
+
+ // Do we need to add an offset to use block 0?
+
+ dev->internalStartBlock = dev->startBlock;
+ dev->internalEndBlock = dev->endBlock;
+ dev->blockOffset = 0;
+ dev->chunkOffset = 0;
+
+ if(dev->startBlock == 0)
+ {
+ dev->internalStartBlock++;
+ dev->internalEndBlock++;
+ dev->blockOffset++;
+ dev->chunkOffset = dev->nChunksPerBlock;
+ }
+
+
+ nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
// OK now calculate a few things for the device
// Calculate chunkGroupBits.
- // We need to find the next power of 2 > than endBlock
+ // We need to find the next power of 2 > than internalEndBlock
- x = dev->nChunksPerBlock * (dev->endBlock+1);
+ x = dev->nChunksPerBlock * (dev->internalEndBlock+1);
for(bits = extraBits = 0; x > 1; bits++)
{
{
dev->chunkGroupBits = bits - 16;
}
+
dev->chunkGroupSize = 1 << dev->chunkGroupBits;
+
+ if(dev->nChunksPerBlock < dev->chunkGroupSize)
+ {
+ // We have a problem because the soft delete won't work if
+ // the chunk group size > chunks per block.
+ // This can be remedied by using larger "virtual blocks".
+
+ return YAFFS_FAIL;
+ }
+
dev->eccUnfixed=0;
dev->tagsEccFixed=0;
dev->tagsEccUnfixed=0;
+ dev->nErasedBlocks=0;
dev->localBuffer = YMALLOC(dev->nBytesPerChunk);
yaffs_DeinitialiseBlocks(dev);
yaffs_DeinitialiseTnodes(dev);
yaffs_DeinitialiseObjects(dev);
+ if(dev->nShortOpCaches > 0)
+ YFREE(dev->srCache);
YFREE(dev->localBuffer);
+ dev->isMounted = 0;
}
}
struct list_head *i;
yaffs_Object *l;
- for(nFree = 0, b = dev->startBlock; b <= dev->endBlock; b++)
+ for(nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; b++)
{
blk = yaffs_GetBlockInfo(dev,b);
+
+
+
+