+
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_Scan ends" TENDSTR)));
+
+ return YAFFS_OK;
+}
+
+
+static int yaffs_ScanBackwards(yaffs_Device *dev)
+{
+ yaffs_ExtendedTags tags;
+ int blk;
+ int blockIterator;
+ int startIterator;
+ int endIterator;
+ int nBlocksToScan = 0;
+
+ int chunk;
+ int c;
+ int deleted;
+ yaffs_BlockState state;
+ yaffs_Object *hardList = NULL;
+ yaffs_Object *hl;
+ yaffs_BlockInfo *bi;
+ int sequenceNumber;
+ yaffs_ObjectHeader *oh;
+ yaffs_Object *in;
+ yaffs_Object *parent;
+ int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+
+ __u8 *chunkData;
+
+ yaffs_BlockIndex *blockIndex = NULL;
+
+
+ if(!dev->isYaffs2)
+ {
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR)));
+ return YAFFS_FAIL;
+ }
+
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards starts intstartblk %d intendblk %d..." TENDSTR),dev->internalStartBlock,dev->internalEndBlock));
+
+ chunkData = yaffs_GetTempBuffer(dev,__LINE__);
+
+
+ dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
+
+ blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
+
+
+ // Scan all the blocks to determine their state
+ for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
+ {
+ bi = yaffs_GetBlockInfo(dev,blk);
+ yaffs_ClearChunkBits(dev,blk);
+ bi->pagesInUse = 0;
+ bi->softDeletions = 0;
+
+ yaffs_QueryInitialBlockState(dev,blk,&state,&sequenceNumber);
+
+ bi->blockState = state;
+ bi->sequenceNumber = sequenceNumber;
+
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block scanning block %d state %d seq %d" TENDSTR),blk,state,sequenceNumber));
+
+ if(state == YAFFS_BLOCK_STATE_DEAD)
+ {
+ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
+ }
+ else if(state == YAFFS_BLOCK_STATE_EMPTY)
+ {
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block empty " TENDSTR)));
+ dev->nErasedBlocks++;
+ dev->nFreeChunks += dev->nChunksPerBlock;
+ }
+ else if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+
+ // Determine the highest sequence number
+ if( dev->isYaffs2 &&
+ sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
+ sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER)
+ {
+
+ blockIndex[nBlocksToScan].seq = sequenceNumber;
+ blockIndex[nBlocksToScan].block = blk;
+
+ nBlocksToScan++;
+
+ if(sequenceNumber >= dev->sequenceNumber)
+ {
+ dev->sequenceNumber = sequenceNumber;
+ }
+ }
+ else if(dev->isYaffs2)
+ {
+ // TODO: Nasty sequence number!
+ T(YAFFS_TRACE_SCAN,(TSTR("Block scanning block %d has bad sequence number %d" TENDSTR),blk,sequenceNumber));
+
+ }
+ }
+ }
+
+ // Sort the blocks
+ // Dungy old bubble sort for now...
+ {
+ yaffs_BlockIndex temp;
+ int i;
+ int j;
+
+ for(i = 0; i < nBlocksToScan; i++)
+ for(j = i+1; j < nBlocksToScan; j++)
+ if(blockIndex[i].seq > blockIndex[j].seq)
+ {
+ temp = blockIndex[j];
+ blockIndex[j] = blockIndex[i];
+ blockIndex[i] = temp;
+ }
+ }
+
+
+ // Now scan the blocks looking at the data.
+ startIterator = 0;
+ endIterator = nBlocksToScan-1;
+ T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("%d blocks to be scanned" TENDSTR),nBlocksToScan));
+
+
+ // For each block.... backwards
+ for(blockIterator = endIterator; blockIterator >= startIterator; blockIterator--)
+ {
+
+ // get the block to scan in the correct order
+ blk = blockIndex[blockIterator].block;
+
+
+ bi = yaffs_GetBlockInfo(dev,blk);
+ state = bi->blockState;
+
+ deleted = 0;
+
+ if( 0 && // Disable since this is redundant.
+ state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+ // Let's look at the first chunk in the block
+ chunk = blk * dev->nChunksPerBlock;
+
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
+
+ // Let's have a good look at this chunk...
+
+ if(!tags.chunkUsed)
+ {
+ // An unassigned chunk in the block
+ // This means that either the block is empty or
+ // this is the one being allocated from
+
+ // We're looking at the first chunk in the block so the block is unused
+ state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->nErasedBlocks++;
+ dev->nFreeChunks += dev->nChunksPerBlock;
+ }
+
+ }
+
+ // For each chunk in each block that needs scanning....
+ for(c = dev->nChunksPerBlock-1; c >= 0 &&
+ (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
+ state == YAFFS_BLOCK_STATE_ALLOCATING); c--)
+ {
+ // Scan backwards...
+ // Read the tags and decide what to do
+ chunk = blk * dev->nChunksPerBlock + c;
+
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
+
+ // Let's have a good look at this chunk...
+
+ if(!tags.chunkUsed)
+ {
+ // An unassigned chunk in the block
+ // This means that either the block is empty or
+ // this is the one being allocated from
+
+ if(c == 0)
+ {
+ // We're looking at the first chunk in the block so the block is unused
+ state = YAFFS_BLOCK_STATE_EMPTY;
+ dev->nErasedBlocks++;
+ }
+ else
+ {
+ // this is the block being allocated from
+ if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+ T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
+ }
+ state = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->allocationBlock = blk;
+ dev->allocationPage = c;
+ dev->allocationBlockFinder = blk; // Set it to here to encourage the allocator to
+ // go forth from here.
+ //Yaffs2 sanity check:
+ // This should be the one with the highest sequence number
+ if(dev->isYaffs2 && (dev->sequenceNumber != bi->sequenceNumber))
+ {
+ T(YAFFS_TRACE_ALWAYS,
+ (TSTR("yaffs: Allocation block %d was not highest sequence id: block seq = %d, dev seq = %d" TENDSTR),
+ blk,bi->sequenceNumber,dev->sequenceNumber));
+ }
+ }
+
+ dev->nFreeChunks ++;
+ }
+ else if(tags.chunkId > 0)
+ {
+ // chunkId > 0 so it is a data chunk...
+ unsigned int endpos;
+
+ __u32 chunkBase = (tags.chunkId - 1)* dev->nBytesPerChunk;
+
+ yaffs_SetChunkBit(dev,blk,c);
+ bi->pagesInUse++;
+
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
+ if(in->variantType == YAFFS_OBJECT_TYPE_FILE &&
+ chunkBase < in->variant.fileVariant.shrinkSize)
+ {
+ // This has not been invalidated by a resize
+ yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,-1);
+
+
+ // File size is calculated by looking at the data chunks if we have not
+ // seen an object header yet. Stop this practice once we find an object header.
+ endpos = (tags.chunkId - 1)* dev->nBytesPerChunk + tags.byteCount;
+ if(!in->valid && // have not got an object header yet
+ in->variant.fileVariant.scannedFileSize <endpos)
+ {
+ in->variant.fileVariant.scannedFileSize = endpos;
+ in->variant.fileVariant.fileSize = in->variant.fileVariant.scannedFileSize;
+ }
+
+ }
+ else
+ {
+ // This chunk has been invalidated by a resize, so delete
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
+
+
+ }
+ //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));
+ }
+ else
+ {
+ // chunkId == 0, so it is an ObjectHeader.
+ // Thus, we read in the object header and make the object
+ yaffs_SetChunkBit(dev,blk,c);
+ bi->pagesInUse++;
+
+ oh = NULL;
+ in = NULL;
+
+ if(tags.extraHeaderInfoAvailable)
+ {
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,tags.extraObjectType);
+ }
+
+
+ if(!in || !in->valid)
+ {
+
+ // If we don't have valid info then we need to read the chunk
+ // TODO In future we can probably defer reading the chunk and
+ // living with invalid data until needed.
+
+ yaffs_ReadChunkWithTagsFromNAND(dev,chunk,chunkData,NULL);
+
+ oh = (yaffs_ObjectHeader *)chunkData;
+
+ if(!in)
+ in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
+
+ }
+
+ if(!in)
+ {
+ // TODO Hoosterman we have a problem!
+ T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: Could not make object for object %d at chunk %d during scan" TENDSTR),tags.objectId,chunk));
+
+ }
+
+ if(in->valid)
+ {
+ // We have already filled this one. We have a duplicate that will be discarded, but
+ // we first have to suck out resize info if it is a file.
+
+ if( (in->variantType == YAFFS_OBJECT_TYPE_FILE) &&
+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
+ (tags.extraHeaderInfoAvailable && tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE))
+ )
+ {
+ __u32 thisSize = (oh) ? oh->fileSize : tags.extraFileLength;
+ __u32 parentObjectId = (oh) ? oh->parentObjectId : tags.extraParentObjectId;
+ unsigned isShrink = (oh) ? oh->isShrink : tags.extraIsShrinkHeader;
+
+ // If it is deleted (unlinked at start also means deleted)
+ // we treat the file size as being zeroed at this point.
+ if(parentObjectId == YAFFS_OBJECTID_DELETED ||
+ parentObjectId == YAFFS_OBJECTID_UNLINKED)
+ {
+ thisSize = 0;
+ isShrink = 1;
+ }
+
+ if(isShrink &&
+ in->variant.fileVariant.shrinkSize > thisSize)
+ {
+ in->variant.fileVariant.shrinkSize = thisSize;
+ }
+
+ if(isShrink)
+ {
+ bi->hasShrinkHeader = 1;
+ }
+
+ }
+ // Use existing - destroy this one.
+ yaffs_DeleteChunk(dev,chunk,1,__LINE__);
+
+ }
+
+ if(!in->valid &&
+ (tags.objectId == YAFFS_OBJECTID_ROOT ||
+ tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
+ {
+ // We only load some info, don't fiddle with directory structure
+ in->valid = 1;
+ in->variantType = oh->type;
+
+ in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+ in->win_atime[0] = oh->win_atime[0];
+ in->win_ctime[0] = oh->win_ctime[0];
+ in->win_mtime[0] = oh->win_mtime[0];
+ in->win_atime[1] = oh->win_atime[1];
+ in->win_ctime[1] = oh->win_ctime[1];
+ in->win_mtime[1] = oh->win_mtime[1];
+#else
+ in->yst_uid = oh->yst_uid;
+ in->yst_gid = oh->yst_gid;
+ in->yst_atime = oh->yst_atime;
+ in->yst_mtime = oh->yst_mtime;
+ in->yst_ctime = oh->yst_ctime;
+ in->yst_rdev = oh->yst_rdev;
+#endif
+ in->chunkId = chunk;
+
+ }
+ else if(!in->valid)
+ {
+ // we need to load this info
+
+ in->valid = 1;
+ in->variantType = oh->type;
+
+ in->yst_mode = oh->yst_mode;
+#ifdef CONFIG_YAFFS_WINCE
+ in->win_atime[0] = oh->win_atime[0];
+ in->win_ctime[0] = oh->win_ctime[0];
+ in->win_mtime[0] = oh->win_mtime[0];
+ in->win_atime[1] = oh->win_atime[1];
+ in->win_ctime[1] = oh->win_ctime[1];
+ in->win_mtime[1] = oh->win_mtime[1];
+#else
+ in->yst_uid = oh->yst_uid;
+ in->yst_gid = oh->yst_gid;
+ in->yst_atime = oh->yst_atime;
+ in->yst_mtime = oh->yst_mtime;
+ in->yst_ctime = oh->yst_ctime;
+ in->yst_rdev = oh->yst_rdev;
+#endif
+ in->chunkId = chunk;
+
+ if(oh->shadowsObject > 0)
+ {
+ yaffs_HandleShadowedObject(dev,oh->shadowsObject,0);
+ }
+
+
+ yaffs_SetObjectName(in,oh->name);
+ in->dirty = 0;
+
+ // directory stuff...
+ // hook up to parent
+
+ parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);
+ if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)
+ {
+ // Set up as a directory
+ parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;
+ INIT_LIST_HEAD(&parent->variant.directoryVariant.children);
+ }
+ else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+ {
+ // Hoosterman, another problem....
+ // We're trying to use a non-directory as a directory
+
+ 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;
+ }
+
+ yaffs_AddObjectToDirectory(parent,in);
+
+ if((parent == dev->deletedDir ||
+ parent == dev->unlinkedDir))
+ {
+ in->deleted = 1; // If it is unlinked at start up then it wants deleting
+ }
+
+ if( oh->isShrink)
+ {
+ // Mark the block as having a shrinkHeader
+ bi->hasShrinkHeader = 1;
+ }
+
+
+ // Note re hardlinks.
+ // Since we might scan a hardlink before its equivalent object is scanned
+ // we put them all in a list.
+ // After scanning is complete, we should have all the objects, so we run through this
+ // list and fix up all the chains.
+
+ switch(in->variantType)
+ {
+ case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem
+ break;
+ case YAFFS_OBJECT_TYPE_FILE:
+
+
+ if(in->variant.fileVariant.scannedFileSize < oh->fileSize)
+ {
+ in->variant.fileVariant.fileSize = oh->fileSize;
+ in->variant.fileVariant.scannedFileSize = in->variant.fileVariant.fileSize;
+ }
+
+ if(oh->isShrink &&
+ in->variant.fileVariant.shrinkSize > oh->fileSize)
+ {
+ in->variant.fileVariant.shrinkSize = oh->fileSize;
+ }
+
+ break;
+ case YAFFS_OBJECT_TYPE_HARDLINK:
+ in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
+ in->hardLinks.next = (struct list_head *)hardList;
+ hardList = in;
+ break;
+ case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing
+ break;
+ case YAFFS_OBJECT_TYPE_SYMLINK: // Do nothing
+ in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
+ break;
+ }
+
+#if 0
+ if(parent == dev->deletedDir)
+ {
+ yaffs_DestroyObject(in);
+ bi->hasShrinkHeader = 1;
+ }
+#endif
+ //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));
+ }
+ }
+ }
+
+ if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
+ {
+ // If we got this far while scanning, then the block is fully allocated.
+ state = YAFFS_BLOCK_STATE_FULL;
+ }
+
+ bi->blockState = state;
+
+ // Now let's see if it was dirty
+ if( bi->pagesInUse == 0 &&
+ !bi->hasShrinkHeader &&
+ bi->blockState == YAFFS_BLOCK_STATE_FULL)
+ {
+ yaffs_BlockBecameDirty(dev,blk);
+ }
+
+ }
+
+ if(blockIndex)
+ {
+ 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);
+
+ }
+
+ }
+
+ {
+ struct list_head *i;
+ struct list_head *n;
+
+ yaffs_Object *l;
+
+ // Soft delete all the unlinked files
+ list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
+ {
+ if(i)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ yaffs_DestroyObject(l);
+ }
+ }
+
+ // Soft delete all the deletedDir files
+ list_for_each_safe(i,n,&dev->deletedDir->variant.directoryVariant.children)
+ {
+ if(i)
+ {
+ l = list_entry(i, yaffs_Object,siblings);
+ yaffs_DestroyObject(l);
+
+ }
+ }
+ }
+
+ yaffs_ReleaseTempBuffer(dev,chunkData,__LINE__);
+
+ T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards ends" TENDSTR)));
+