X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_guts.c;h=4af4c4094a4d348446870e070d79ef62c09d3f95;hp=d89b87af44eced13f0f7bd0a584581cc579f2914;hb=53c45617c6cbeffcafce1e96e868fcf516e62373;hpb=c8b56f564ace4a2052d1e74e2b96c6532dd4ef44 diff --git a/yaffs_guts.c b/yaffs_guts.c index d89b87a..4af4c40 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.82 2009-03-09 04:24:17 charles Exp $"; + "$Id: yaffs_guts.c,v 1.99 2009-12-14 22:03:05 charles Exp $"; #include "yportenv.h" @@ -49,6 +49,7 @@ static void yaffs_HandleUpdateChunk(yaffs_Device *dev, int chunkInNAND, const yaffs_ExtendedTags *tags); /* Other local prototypes */ +static void yaffs_UpdateParent(yaffs_Object *obj); static int yaffs_UnlinkObject(yaffs_Object *obj); static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj); @@ -112,6 +113,11 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId); +static void yaffs_SkipRestOfBlock(yaffs_Device *dev); +static int yaffs_VerifyChunkWritten(yaffs_Device *dev, + int chunkInNAND, + const __u8 *data, + yaffs_ExtendedTags *tags); /* Function to calculate chunk and offset */ @@ -759,7 +765,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, @@ -918,6 +924,29 @@ static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, } + +static int yaffs_VerifyChunkWritten(yaffs_Device *dev, + int chunkInNAND, + const __u8 *data, + yaffs_ExtendedTags *tags) +{ + int retval = YAFFS_OK; + yaffs_ExtendedTags tempTags; + __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__); + int result; + + result = yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND,buffer,&tempTags); + if(memcmp(buffer,data,dev->nDataBytesPerChunk) || + tempTags.objectId != tags->objectId || + tempTags.chunkId != tags->chunkId || + tempTags.byteCount != tags->byteCount) + retval = YAFFS_FAIL; + + yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); + + return retval; +} + static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_ExtendedTags *tags, @@ -957,12 +986,11 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, * chunk due to power loss. This checking policy should * catch that case with very few checks and thus save a * lot of checks that are most likely not needed. + * + * Mods to the above + * If an erase check fails or the write fails we skip the + * rest of the block. */ - if (bi->gcPrioritise) { - yaffs_DeleteChunk(dev, chunk, 1, __LINE__); - /* try another chunk */ - continue; - } /* let's give it a try */ attempts++; @@ -977,20 +1005,30 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, (TSTR("**>> yaffs chunk %d was not erased" TENDSTR), chunk)); - /* try another chunk */ + /* If not erased, delete this one, + * skip rest of block and + * try another chunk */ + yaffs_DeleteChunk(dev,chunk,1,__LINE__); + yaffs_SkipRestOfBlock(dev); continue; } - bi->skipErasedCheck = 1; } writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, data, tags); + + if(!bi->skipErasedCheck) + writeOk = yaffs_VerifyChunkWritten(dev, chunk, data, tags); + if (writeOk != YAFFS_OK) { + /* Clean up aborted write, skip to next block and + * try another chunk */ yaffs_HandleWriteChunkError(dev, chunk, erasedOk); - /* try another chunk */ continue; } + bi->skipErasedCheck = 1; + /* Copy the data into the robustification buffer */ yaffs_HandleWriteChunkOk(dev, chunk, data, tags); @@ -1100,6 +1138,7 @@ static void yaffs_HandleWriteChunkError(yaffs_Device *dev, int chunkInNAND, /* Delete the chunk */ yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); + yaffs_SkipRestOfBlock(dev); } @@ -1130,7 +1169,7 @@ static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name) { #ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM memset(obj->shortName, 0, sizeof(YCHAR) * (YAFFS_SHORT_NAME_LENGTH+1)); - if (name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH) + if (name && yaffs_strnlen(name,YAFFS_SHORT_NAME_LENGTH+1) <= YAFFS_SHORT_NAME_LENGTH) yaffs_strcpy(obj->shortName, name); else obj->shortName[0] = _Y('\0'); @@ -1323,7 +1362,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; @@ -1481,13 +1520,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 */ @@ -1506,6 +1545,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 */ @@ -1518,6 +1559,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; } } @@ -1544,11 +1587,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++; @@ -1636,7 +1684,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); } } @@ -1713,7 +1761,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); } } @@ -1855,10 +1903,14 @@ static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects) list = YMALLOC(sizeof(yaffs_ObjectList)); if (!newObjects || !list) { - if (newObjects) + if (newObjects){ YFREE(newObjects); - if (list) + newObjects = NULL; + } + if (list){ YFREE(list); + list = NULL; + } T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Could not allocate more objects" TENDSTR))); return YAFFS_FAIL; @@ -2002,6 +2054,7 @@ static void yaffs_FreeObject(yaffs_Object *tn) #ifdef VALGRIND_TEST YFREE(tn); + tn = NULL; #else /* Link into the free list. */ tn->siblings.next = (struct ylist_head *)(dev->freeObjects); @@ -2164,18 +2217,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; @@ -2241,13 +2296,17 @@ static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, static YCHAR *yaffs_CloneString(const YCHAR *str) { YCHAR *newStr = NULL; + int len; - if (str && *str) { - newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR)); - if (newStr) - yaffs_strcpy(newStr, str); - } + if (!str) + str = _Y(""); + len = yaffs_strnlen(str,YAFFS_MAX_ALIAS_LENGTH); + newStr = YMALLOC((len + 1) * sizeof(YCHAR)); + if (newStr){ + yaffs_strncpy(newStr, str,len); + newStr[len] = 0; + } return newStr; } @@ -2255,7 +2314,7 @@ static YCHAR *yaffs_CloneString(const YCHAR *str) /* * Mknod (create) a new object. * equivalentObject only has meaning for a hard link; - * aliasString only has meaning for a sumlink. + * aliasString only has meaning for a symlink. * rdev only has meaning for devices (a subset of special objects) */ @@ -2277,19 +2336,21 @@ 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) { - yaffs_FreeObject(in); + if (!str) return NULL; - } } + in = yaffs_CreateNewObject(dev, -1, type); + + if (!in){ + if(str) + YFREE(str); + return NULL; + } + + if (in) { @@ -2345,6 +2406,7 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, in = NULL; } + yaffs_UpdateParent(parent); } return in; @@ -2459,6 +2521,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) @@ -2466,6 +2530,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. @@ -2475,7 +2541,7 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, force = 1; #endif - else if (yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH) + if(yaffs_strnlen(newName,YAFFS_MAX_NAME_LENGTH+1) > YAFFS_MAX_NAME_LENGTH) /* ENAMETOOLONG */ return YAFFS_FAIL; @@ -2493,14 +2559,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; } - return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); + result = yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); + + yaffs_UpdateParent(oldDir); + if(newDir != oldDir) + yaffs_UpdateParent(newDir); + + return result; } return YAFFS_FAIL; } @@ -2940,6 +3018,22 @@ static int yaffs_GetErasedChunks(yaffs_Device *dev) } +/* + * yaffs_SkipRestOfBlock() skips over the rest of the allocation block + * if we don't want to write to it. + */ +static void yaffs_SkipRestOfBlock(yaffs_Device *dev) +{ + if(dev->allocationBlock > 0){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, dev->allocationBlock); + if(bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING){ + bi->blockState = YAFFS_BLOCK_STATE_FULL; + dev->allocationBlock = -1; + } + } +} + + static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, int wholeBlock) { @@ -2964,7 +3058,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), @@ -2975,12 +3068,16 @@ 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 * they're going to get really deleted during GC. */ - dev->nFreeChunks -= bi->softDeletions; + if(dev->gcChunk == 0) /* first time through for this block */ + dev->nFreeChunks -= bi->softDeletions; dev->isDoingGC = 1; @@ -3096,6 +3193,7 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, if (tags.chunkId == 0) { /* It is an object Id, * We need to nuke the shrinkheader flags first + * Also need to clean up shadowing. * We no longer want the shrinkHeader flag since its work is done * and if it is left in place it will mess up scanning. */ @@ -3104,6 +3202,9 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, oh = (yaffs_ObjectHeader *)buffer; oh->isShrink = 0; tags.extraIsShrinkHeader = 0; + oh->shadowsObject = 0; + oh->inbandShadowsObject = 0; + tags.extraShadows = 0; yaffs_VerifyObjectHeader(object, oh, &tags, 1); } @@ -3328,7 +3429,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; @@ -3398,6 +3499,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; @@ -3429,6 +3532,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); @@ -3510,7 +3618,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; } @@ -3624,7 +3732,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) { @@ -3632,15 +3740,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); @@ -3667,6 +3783,7 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, int newChunkId; yaffs_ExtendedTags newTags; yaffs_ExtendedTags oldTags; + YCHAR *alias = NULL; __u8 *buffer = NULL; YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1]; @@ -3726,7 +3843,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)); @@ -3755,8 +3872,11 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force, /* Do nothing */ break; case YAFFS_OBJECT_TYPE_SYMLINK: + alias = in->variant.symLinkVariant.alias; + if(!alias) + alias = _Y("no alias"); yaffs_strncpy(oh->alias, - in->variant.symLinkVariant.alias, + alias, YAFFS_MAX_ALIAS_LENGTH); oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; break; @@ -3784,13 +3904,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__); } @@ -5018,11 +5138,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, @@ -5033,13 +5155,17 @@ int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize) loff_t yaffs_GetFileSize(yaffs_Object *obj) { + YCHAR *alias = NULL; obj = yaffs_GetEquivalentObject(obj); switch (obj->variantType) { case YAFFS_OBJECT_TYPE_FILE: return obj->variant.fileVariant.fileSize; case YAFFS_OBJECT_TYPE_SYMLINK: - return yaffs_strlen(obj->variant.symLinkVariant.alias); + alias = obj->variant.symLinkVariant.alias; + if(!alias) + return 0; + return yaffs_strnlen(alias,YAFFS_MAX_ALIAS_LENGTH); default: return 0; } @@ -5047,23 +5173,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; } @@ -5164,19 +5294,26 @@ 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) { - YFREE(in->variant.symLinkVariant.alias); + if(in->variant.symLinkVariant.alias) + YFREE(in->variant.symLinkVariant.alias); + in->variant.symLinkVariant.alias=NULL; return yaffs_DoGenericObjectDeletion(in); } @@ -5230,6 +5367,9 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj) immediateDeletion = 1; #endif + if(obj) + yaffs_UpdateParent(obj->parent); + if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { return yaffs_DeleteHardLink(obj); } else if (!ylist_empty(&obj->hardLinks)) { @@ -5284,7 +5424,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); } @@ -5323,7 +5465,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; } @@ -5335,6 +5478,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 */ @@ -5427,6 +5571,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; @@ -5550,7 +5813,7 @@ static int yaffs_Scan(yaffs_Device *dev) dev->allocationBlock = blk; dev->allocationPage = c; dev->allocationBlockFinder = blk; - /* Set it to here to encourage the allocator to go forth from here. */ + /* Set block finder here to encourage the allocator to go forth from here. */ } @@ -5796,12 +6059,6 @@ static int yaffs_Scan(yaffs_Device *dev) break; } -/* - if (parent == dev->deletedDir) { - yaffs_DestroyObject(in); - bi->hasShrinkHeader = 1; - } -*/ } } } @@ -5811,6 +6068,12 @@ static int yaffs_Scan(yaffs_Device *dev) state = YAFFS_BLOCK_STATE_FULL; } + if (state == YAFFS_BLOCK_STATE_ALLOCATING) { + /* If the block was partially allocated then treat it as fully allocated.*/ + state = YAFFS_BLOCK_STATE_FULL; + dev->allocationBlock = -1; + } + bi->blockState = state; /* Now let's see if it was dirty */ @@ -6153,13 +6416,9 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) dev->allocationBlockFinder = blk; } else { /* This is a partially written block that is not - * the current allocation block. This block must have - * had a write failure, so set up for retirement. + * the current allocation block. */ - /* bi->needsRetiring = 1; ??? TODO */ - bi->gcPrioritise = 1; - T(YAFFS_TRACE_ALWAYS, (TSTR("Partially written block %d detected" TENDSTR), blk)); @@ -6252,9 +6511,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 || @@ -6423,6 +6680,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) oh-> shadowsObject, 1); + yaffs_SetObjectName(in, oh->name); @@ -6554,6 +6812,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) state = YAFFS_BLOCK_STATE_FULL; } + bi->blockState = state; /* Now let's see if it was dirty */ @@ -6564,6 +6823,8 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) } } + + yaffs_SkipRestOfBlock(dev); if (altBlockIndex) YFREE_ALT(blockIndex); @@ -6666,6 +6927,26 @@ static void yaffs_VerifyDirectory(yaffs_Object *directory) } } +/* + *yaffs_UpdateParent() handles fixing a directories mtime and ctime when a new + * link (ie. name) is created or deleted in the directory. + * + * ie. + * create dir/a : update dir's mtime/ctime + * rm dir/a: update dir's mtime/ctime + * modify dir/a: don't update dir's mtimme/ctime + */ + +static void yaffs_UpdateParent(yaffs_Object *obj) +{ + if(!obj) + return; + + obj->dirty = 1; + obj->yst_mtime = obj->yst_ctime = Y_CURRENT_TIME; + + yaffs_UpdateObjectHeader(obj,NULL,0,0,0); +} static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj) { @@ -6683,11 +6964,10 @@ static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj) ylist_del_init(&obj->siblings); obj->parent = NULL; - + yaffs_VerifyDirectory(parent); } - static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj) { @@ -6781,7 +7061,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; } @@ -6869,7 +7149,7 @@ int yaffs_GetObjectName(yaffs_Object *obj, YCHAR *name, int buffSize) } #ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM else if (obj->shortName[0]) - yaffs_strcpy(name, obj->shortName); + yaffs_strncpy(name, obj->shortName,YAFFS_SHORT_NAME_LENGTH+1); #endif else { int result; @@ -6885,11 +7165,12 @@ int yaffs_GetObjectName(yaffs_Object *obj, YCHAR *name, int buffSize) NULL); } yaffs_strncpy(name, oh->name, buffSize - 1); + name[buffSize-1]=0; yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__); } - return yaffs_strlen(name); + return yaffs_strnlen(name,buffSize-1); } int yaffs_GetObjectFileLength(yaffs_Object *obj) @@ -6899,9 +7180,11 @@ int yaffs_GetObjectFileLength(yaffs_Object *obj) if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) return obj->variant.fileVariant.fileSize; - if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) - return yaffs_strlen(obj->variant.symLinkVariant.alias); - else { + if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK){ + if(!obj->variant.symLinkVariant.alias) + return 0; + return yaffs_strnlen(obj->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH); + } else { /* Only a directory should drop through to here */ return obj->myDev->nDataBytesPerChunk; } @@ -7030,7 +7313,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 @@ -7371,6 +7654,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) { @@ -7394,6 +7680,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)));