X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_guts.c;h=0dc9622a6ec34ce013742cda6555a242160a7b6a;hp=33ba5e652745d0dae67c2d4bc4de30ee1fdaeb86;hb=96aed5b90237cda3b8168a957be33c83ae192794;hpb=51bd6245e2a5ffab0befa6aace5510b6e45ffa27 diff --git a/yaffs_guts.c b/yaffs_guts.c index 33ba5e6..0dc9622 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -12,7 +12,7 @@ */ const char *yaffs_guts_c_version = - "$Id: yaffs_guts.c,v 1.85 2009-06-08 23:50:44 charles Exp $"; + "$Id: yaffs_guts.c,v 1.94 2009-11-10 23:55:05 charles Exp $"; #include "yportenv.h" @@ -113,7 +113,6 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId); - /* Function to calculate chunk and offset */ static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, int *chunkOut, @@ -760,7 +759,7 @@ static void yaffs_VerifyObject(yaffs_Object *obj) chunkMax = (dev->internalEndBlock+1) * dev->nChunksPerBlock - 1; chunkInRange = (((unsigned)(obj->hdrChunk)) >= chunkMin && ((unsigned)(obj->hdrChunk)) <= chunkMax); - chunkIdOk = chunkInRange || obj->hdrChunk == 0; + chunkIdOk = chunkInRange || (obj->hdrChunk == 0); chunkValid = chunkInRange && yaffs_CheckChunkBit(dev, obj->hdrChunk / dev->nChunksPerBlock, @@ -1324,7 +1323,7 @@ static void yaffs_InitialiseTnodes(yaffs_Device *dev) } -void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, +void yaffs_LoadLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val) { __u32 *map = (__u32 *)tn; @@ -1482,13 +1481,13 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, if (tn) { tn->internal[0] = fStruct->top; fStruct->top = tn; + fStruct->topLevel++; } else { T(YAFFS_TRACE_ERROR, - (TSTR("yaffs: no more tnodes" TENDSTR))); + (TSTR("yaffs: no more tnodes" TENDSTR))); + return NULL; } } - - fStruct->topLevel = requiredTallness; } /* Traverse down to level 0, adding anything we need */ @@ -1507,6 +1506,8 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, if ((l > 1) && !tn->internal[x]) { /* Add missing non-level-zero tnode */ tn->internal[x] = yaffs_GetTnode(dev); + if(!tn->internal[x]) + return NULL; } else if (l == 1) { /* Looking from level 1 at level 0 */ @@ -1519,6 +1520,8 @@ static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, } else if (!tn->internal[x]) { /* Don't have one, none passed in */ tn->internal[x] = yaffs_GetTnode(dev); + if(!tn->internal[x]) + return NULL; } } @@ -1545,11 +1548,16 @@ static int yaffs_FindChunkInGroup(yaffs_Device *dev, int theChunk, for (j = 0; theChunk && j < dev->chunkGroupSize; j++) { if (yaffs_CheckChunkBit(dev, theChunk / dev->nChunksPerBlock, theChunk % dev->nChunksPerBlock)) { - yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, - tags); - if (yaffs_TagsMatch(tags, objectId, chunkInInode)) { - /* found it; */ + + if(dev->chunkGroupSize == 1) return theChunk; + else { + yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, + tags); + if (yaffs_TagsMatch(tags, objectId, chunkInInode)) { + /* found it; */ + return theChunk; + } } } theChunk++; @@ -1637,7 +1645,7 @@ static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, } - yaffs_PutLevel0Tnode(dev, tn, i, 0); + yaffs_LoadLevel0Tnode(dev, tn, i, 0); } } @@ -1714,7 +1722,7 @@ static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, * a block. */ yaffs_SoftDeleteChunk(dev, theChunk); - yaffs_PutLevel0Tnode(dev, tn, i, 0); + yaffs_LoadLevel0Tnode(dev, tn, i, 0); } } @@ -2165,18 +2173,20 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev, int number, if (number < 0) number = yaffs_CreateNewObjectNumber(dev); - theObject = yaffs_AllocateEmptyObject(dev); - if (!theObject) - return NULL; - if (type == YAFFS_OBJECT_TYPE_FILE) { tn = yaffs_GetTnode(dev); - if (!tn) { - yaffs_FreeObject(theObject); + if (!tn) return NULL; - } } + theObject = yaffs_AllocateEmptyObject(dev); + if (!theObject){ + if(tn) + yaffs_FreeTnode(dev,tn); + return NULL; + } + + if (theObject) { theObject->fake = 0; theObject->renameAllowed = 1; @@ -2278,11 +2288,6 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, if (yaffs_FindObjectByName(parent, name)) return NULL; - in = yaffs_CreateNewObject(dev, -1, type); - - if (!in) - return YAFFS_FAIL; - if (type == YAFFS_OBJECT_TYPE_SYMLINK) { str = yaffs_CloneString(aliasString); if (!str) { @@ -2291,6 +2296,15 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, } } + in = yaffs_CreateNewObject(dev, -1, type); + + if (!in){ + if(str) + YFREE(str); + return NULL; + } + + if (in) { @@ -2461,6 +2475,8 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, yaffs_Object *obj = NULL; yaffs_Object *existingTarget = NULL; int force = 0; + int result; + yaffs_Device *dev; if (!oldDir || oldDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) @@ -2468,6 +2484,8 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, if (!newDir || newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) YBUG(); + dev = oldDir->myDev; + #ifdef CONFIG_YAFFS_CASE_INSENSITIVE /* Special case for case insemsitive systems (eg. WinCE). * While look-up is case insensitive, the name isn't. @@ -2477,7 +2495,7 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, force = 1; #endif - else if (yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH) + if(yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH) /* ENAMETOOLONG */ return YAFFS_FAIL; @@ -2495,17 +2513,26 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, return YAFFS_FAIL; /* EEXIST or ENOTEMPTY */ } else if (existingTarget && existingTarget != obj) { /* Nuke the target first, using shadowing, - * but only if it isn't the same object + * but only if it isn't the same object. + * + * Note we must disable gc otherwise it can mess up the shadowing. + * */ + dev->isDoingGC=1; yaffs_ChangeObjectName(obj, newDir, newName, force, existingTarget->objectId); + existingTarget->isShadowed = 1; yaffs_UnlinkObject(existingTarget); + dev->isDoingGC=0; } + + result = yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); + yaffs_UpdateParent(oldDir); if(newDir != oldDir) yaffs_UpdateParent(newDir); - - return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); + + return result; } return YAFFS_FAIL; } @@ -2969,7 +2996,6 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT); - bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; T(YAFFS_TRACE_TRACING, (TSTR("Collecting block %d, in use %d, shrink %d, wholeBlock %d" TENDSTR), @@ -2980,6 +3006,9 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, /*yaffs_VerifyFreeChunks(dev); */ + if(bi->blockState == YAFFS_BLOCK_STATE_FULL) + bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; + bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */ /* Take off the number of soft deleted entries because @@ -3334,7 +3363,7 @@ static int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in, int chunkInInode, /* Delete the entry in the filestructure (if found) */ if (retVal != -1) - yaffs_PutLevel0Tnode(dev, tn, chunkInInode, 0); + yaffs_LoadLevel0Tnode(dev, tn, chunkInInode, 0); } return retVal; @@ -3404,6 +3433,8 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, /* NB inScan is zero unless scanning. * For forward scanning, inScan is > 0; * for backward scanning inScan is < 0 + * + * chunkInNAND = 0 is a dummy insert to make sure the tnodes are there. */ yaffs_Tnode *tn; @@ -3435,6 +3466,11 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, NULL); if (!tn) return YAFFS_FAIL; + + if(!chunkInNAND) + /* Dummy insert, bail now */ + return YAFFS_OK; + existingChunk = yaffs_GetChunkGroupBase(dev, tn, chunkInInode); @@ -3516,7 +3552,7 @@ static int yaffs_PutChunkIntoFile(yaffs_Object *in, int chunkInInode, if (existingChunk == 0) in->nDataChunks++; - yaffs_PutLevel0Tnode(dev, tn, chunkInInode, chunkInNAND); + yaffs_LoadLevel0Tnode(dev, tn, chunkInInode, chunkInNAND); return YAFFS_OK; } @@ -3630,7 +3666,7 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode, newTags.chunkId = chunkInInode; newTags.objectId = in->objectId; newTags.serialNumber = - (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1; + (prevChunkId > 0) ? prevTags.serialNumber + 1 : 1; newTags.byteCount = nBytes; if (nBytes < 1 || nBytes > dev->totalBytesPerChunk) { @@ -3638,15 +3674,23 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode, (TSTR("Writing %d bytes to chunk!!!!!!!!!" TENDSTR), nBytes)); YBUG(); } - + + /* + * If there isn't already a chunk there then do a dummy + * insert to make sue we have the desired tnode structure. + */ + if(prevChunkId < 1 && + yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0) != YAFFS_OK) + return -1; + newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, useReserve); - if (newChunkId >= 0) { + if (newChunkId > 0) { yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0); - if (prevChunkId >= 0) + if (prevChunkId > 0) yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__); yaffs_CheckFileSanity(in); @@ -3732,7 +3776,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, if (name && *name) { memset(oh->name, 0, sizeof(oh->name)); yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); - } else if (prevChunkId >= 0) + } else if (prevChunkId > 0) memcpy(oh->name, oldName, sizeof(oh->name)); else memset(oh->name, 0, sizeof(oh->name)); @@ -3790,13 +3834,13 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, /* Create new chunk in NAND */ newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, - (prevChunkId >= 0) ? 1 : 0); + (prevChunkId > 0) ? 1 : 0); if (newChunkId >= 0) { in->hdrChunk = newChunkId; - if (prevChunkId >= 0) { + if (prevChunkId > 0) { yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__); } @@ -5024,11 +5068,13 @@ int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize) } - /* Write a new object header. + /* Write a new object header to reflect the resize. * show we've shrunk the file, if need be - * Do this only if the file is not in the deleted directories. + * Do this only if the file is not in the deleted directories + * and is not shadowed. */ if (in->parent && + !in->isShadowed && in->parent->objectId != YAFFS_OBJECTID_UNLINKED && in->parent->objectId != YAFFS_OBJECTID_DELETED) yaffs_UpdateObjectHeader(in, NULL, 0, @@ -5053,23 +5099,27 @@ loff_t yaffs_GetFileSize(yaffs_Object *obj) -int yaffs_FlushFile(yaffs_Object *in, int updateTime) +int yaffs_FlushFile(yaffs_Object *in, int updateTime, int dataSync) { int retVal; if (in->dirty) { yaffs_FlushFilesChunkCache(in); - if (updateTime) { + if(dataSync) /* Only sync data */ + retVal=YAFFS_OK; + else { + if (updateTime) { #ifdef CONFIG_YAFFS_WINCE - yfsd_WinFileTimeNow(in->win_mtime); + yfsd_WinFileTimeNow(in->win_mtime); #else - in->yst_mtime = Y_CURRENT_TIME; + in->yst_mtime = Y_CURRENT_TIME; #endif - } + } - retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >= - 0) ? YAFFS_OK : YAFFS_FAIL; + retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >= + 0) ? YAFFS_OK : YAFFS_FAIL; + } } else { retVal = YAFFS_OK; } @@ -5170,14 +5220,19 @@ int yaffs_DeleteFile(yaffs_Object *in) } } -static int yaffs_DeleteDirectory(yaffs_Object *in) +static int yaffs_IsNonEmptyDirectory(yaffs_Object *obj) { - /* First check that the directory is empty. */ - if (ylist_empty(&in->variant.directoryVariant.children)) - return yaffs_DoGenericObjectDeletion(in); + return (obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) && + !(ylist_empty(&obj->variant.directoryVariant.children)); +} - return YAFFS_FAIL; +static int yaffs_DeleteDirectory(yaffs_Object *obj) +{ + /* First check that the directory is empty. */ + if (yaffs_IsNonEmptyDirectory(obj)) + return YAFFS_FAIL; + return yaffs_DoGenericObjectDeletion(obj); } static int yaffs_DeleteSymLink(yaffs_Object *in) @@ -5293,7 +5348,9 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj) default: return YAFFS_FAIL; } - } else + } else if(yaffs_IsNonEmptyDirectory(obj)) + return YAFFS_FAIL; + else return yaffs_ChangeObjectName(obj, obj->myDev->unlinkedDir, _Y("unlinked"), 0, 0); } @@ -5332,7 +5389,8 @@ static void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, /* Handle YAFFS2 case (backward scanning) * If the shadowed object exists then ignore. */ - if (yaffs_FindObjectByNumber(dev, objId)) + obj = yaffs_FindObjectByNumber(dev, objId); + if(obj) return; } @@ -5344,6 +5402,7 @@ static void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, YAFFS_OBJECT_TYPE_FILE); if (!obj) return; + obj->isShadowed = 1; yaffs_AddObjectToDirectory(dev->unlinkedDir, obj); obj->variant.fileVariant.shrinkSize = 0; obj->valid = 1; /* So that we don't read any other info for this file */ @@ -5436,6 +5495,125 @@ static void yaffs_StripDeletedObjects(yaffs_Device *dev) } +/* + * This code iterates through all the objects making sure that they are rooted. + * Any unrooted objects are re-rooted in lost+found. + * An object needs to be in one of: + * - Directly under deleted, unlinked + * - Directly or indirectly under root. + * + * Note: + * This code assumes that we don't ever change the current relationships between + * directories: + * rootDir->parent == unlinkedDir->parent == deletedDir->parent == NULL + * lostNfound->parent == rootDir + * + * This fixes the problem where directories might have inadvertently been deleted + * leaving the object "hanging" without being rooted in the directory tree. + */ + +static int yaffs_HasNULLParent(yaffs_Device *dev, yaffs_Object *obj) +{ + return (obj == dev->deletedDir || + obj == dev->unlinkedDir|| + obj == dev->rootDir); +} + +static void yaffs_FixHangingObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + yaffs_Object *parent; + int i; + struct ylist_head *lh; + struct ylist_head *n; + int depthLimit; + int hanging; + + + /* Iterate through the objects in each hash entry, + * looking at each object. + * Make sure it is rooted. + */ + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + ylist_for_each_safe(lh, n, &dev->objectBucket[i].list) { + if (lh) { + obj = ylist_entry(lh, yaffs_Object, hashLink); + parent= obj->parent; + + if(yaffs_HasNULLParent(dev,obj)){ + /* These directories are not hanging */ + hanging = 0; + } + else if(!parent || parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) + hanging = 1; + else if(yaffs_HasNULLParent(dev,parent)) + hanging = 0; + else { + /* + * Need to follow the parent chain to see if it is hanging. + */ + hanging = 0; + depthLimit=100; + + while(parent != dev->rootDir && + parent->parent && + parent->parent->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && + depthLimit > 0){ + parent = parent->parent; + depthLimit--; + } + if(parent != dev->rootDir) + hanging = 1; + } + if(hanging){ + T(YAFFS_TRACE_SCAN, + (TSTR("Hanging object %d moved to lost and found" TENDSTR), + obj->objectId)); + yaffs_AddObjectToDirectory(dev->lostNFoundDir,obj); + } + } + } + } +} + + +/* + * Delete directory contents for cleaning up lost and found. + */ +static void yaffs_DeleteDirectoryContents(yaffs_Object *dir) +{ + yaffs_Object *obj; + struct ylist_head *lh; + struct ylist_head *n; + + if(dir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) + YBUG(); + + ylist_for_each_safe(lh, n, &dir->variant.directoryVariant.children) { + if (lh) { + obj = ylist_entry(lh, yaffs_Object, siblings); + if(obj->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) + yaffs_DeleteDirectoryContents(obj); + + T(YAFFS_TRACE_SCAN, + (TSTR("Deleting lost_found object %d" TENDSTR), + obj->objectId)); + + /* Need to use UnlinkObject since Delete would not handle + * hardlinked objects correctly. + */ + yaffs_UnlinkObject(obj); + } + } + +} + +static void yaffs_EmptyLostAndFound(yaffs_Device *dev) +{ + yaffs_DeleteDirectoryContents(dev->lostNFoundDir); +} + static int yaffs_Scan(yaffs_Device *dev) { yaffs_ExtendedTags tags; @@ -6261,9 +6439,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) } if (!in || -#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD - !in->valid || -#endif + (!in->valid && dev->disableLazyLoad) || tags.extraShadows || (!in->valid && (tags.objectId == YAFFS_OBJECTID_ROOT || @@ -6809,7 +6985,7 @@ yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory, * Do a real check */ yaffs_GetObjectName(l, buffer, - YAFFS_MAX_NAME_LENGTH); + YAFFS_MAX_NAME_LENGTH + 1); if (yaffs_strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH) == 0) return l; } @@ -7058,7 +7234,7 @@ int yaffs_DumpObject(yaffs_Object *obj) { YCHAR name[257]; - yaffs_GetObjectName(obj, name, 256); + yaffs_GetObjectName(obj, name, YAFFS_MAX_NAME_LENGTH + 1); T(YAFFS_TRACE_ALWAYS, (TSTR @@ -7399,6 +7575,9 @@ int yaffs_GutsInitialise(yaffs_Device *dev) init_failed = 1; yaffs_StripDeletedObjects(dev); + yaffs_FixHangingObjects(dev); + if(dev->emptyLostAndFound) + yaffs_EmptyLostAndFound(dev); } if (init_failed) { @@ -7422,6 +7601,9 @@ int yaffs_GutsInitialise(yaffs_Device *dev) yaffs_VerifyFreeChunks(dev); yaffs_VerifyBlocks(dev); + /* Clean up any aborted checkpoint data */ + if(!dev->isCheckpointed && dev->blocksInCheckpoint > 0) + yaffs_InvalidateCheckpoint(dev); T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR)));