/*
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
* Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* XXX U-BOOT XXX */
#include <common.h>
const char *yaffs_guts_c_version =
"$Id: yaffs_guts.c,v 1.52 2007/10/16 00:45:05 charles Exp $";
#include "yportenv.h"
#include "linux/stat.h"
#include "yaffsinterface.h"
#include "yaffsfs.h"
#include "yaffs_guts.h"
#include "yaffs_tagsvalidity.h"
#include "yaffs_tagscompat.h"
#ifndef CONFIG_YAFFS_USE_OWN_SORT
#include "yaffs_qsort.h"
#endif
#include "yaffs_nand.h"
#include "yaffs_checkptrw.h"
#include "yaffs_nand.h"
#include "yaffs_packedtags2.h"
#include "malloc.h"
#ifdef CONFIG_YAFFS_WINCE
void yfsd_LockYAFFS(BOOL fsLockOnly);
void yfsd_UnlockYAFFS(BOOL fsLockOnly);
#endif
#define YAFFS_PASSIVE_GC_CHUNKS 2
#include "yaffs_ecc.h"
/* Robustification (if it ever comes about...) */
static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND);
static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk);
static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
const __u8 * data,
const yaffs_ExtendedTags * tags);
static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
const yaffs_ExtendedTags * tags);
/* Other local prototypes */
static int yaffs_UnlinkObject( yaffs_Object *obj);
static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj);
static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList);
static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev,
const __u8 * buffer,
yaffs_ExtendedTags * tags,
int useReserve);
static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode,
int chunkInNAND, int inScan);
static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number,
yaffs_ObjectType type);
static void yaffs_AddObjectToDirectory(yaffs_Object * directory,
yaffs_Object * obj);
static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name,
int force, int isShrink, int shadows);
static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj);
static int yaffs_CheckStructures(void);
static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level,
int chunkOffset, int *limit);
static int yaffs_DoGenericObjectDeletion(yaffs_Object * in);
static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo);
static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo);
static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
int lineNo);
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
int chunkInNAND);
static int yaffs_UnlinkWorker(yaffs_Object * obj);
static void yaffs_DestroyObject(yaffs_Object * obj);
static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
int chunkInObject);
loff_t yaffs_GetFileSize(yaffs_Object * obj);
static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr);
static void yaffs_VerifyFreeChunks(yaffs_Device * dev);
static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in);
#ifdef YAFFS_PARANOID
static int yaffs_CheckFileSanity(yaffs_Object * in);
#else
#define yaffs_CheckFileSanity(in)
#endif
static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in);
static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId);
static void yaffs_InvalidateCheckpoint(yaffs_Device *dev);
static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode,
yaffs_ExtendedTags * tags);
static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos);
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, __u32 *chunk, __u32 *offset)
{
if(dev->chunkShift){
/* Easy-peasy power of 2 case */
*chunk = (__u32)(addr >> dev->chunkShift);
*offset = (__u32)(addr & dev->chunkMask);
}
else if(dev->crumbsPerChunk)
{
/* Case where we're using "crumbs" */
*offset = (__u32)(addr & dev->crumbMask);
addr >>= dev->crumbShift;
*chunk = ((__u32)addr)/dev->crumbsPerChunk;
*offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift);
}
else
YBUG();
}
/* Function to return the number of shifts for a power of 2 greater than or equal
* to the given number
* Note we don't try to cater for all possible numbers and this does not have to
* be hellishly efficient.
*/
static __u32 ShiftsGE(__u32 x)
{
int extraBits;
int nShifts;
nShifts = extraBits = 0;
while(x>1){
if(x & 1) extraBits++;
x>>=1;
nShifts++;
}
if(extraBits)
nShifts++;
return nShifts;
}
/* Function to return the number of shifts to get a 1 in bit 0
*/
static __u32 ShiftDiv(__u32 x)
{
int nShifts;
nShifts = 0;
if(!x) return 0;
while( !(x&1)){
x>>=1;
nShifts++;
}
return nShifts;
}
/*
* Temporary buffer manipulations.
*/
static int yaffs_InitialiseTempBuffers(yaffs_Device *dev)
{
int i;
__u8 *buf = (__u8 *)1;
memset(dev->tempBuffer,0,sizeof(dev->tempBuffer));
for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) {
dev->tempBuffer[i].line = 0; /* not in use */
dev->tempBuffer[i].buffer = buf =
YMALLOC_DMA(dev->nDataBytesPerChunk);
}
return buf ? YAFFS_OK : YAFFS_FAIL;
}
static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo)
{
int i, j;
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
if (dev->tempBuffer[i].line == 0) {
dev->tempBuffer[i].line = lineNo;
if ((i + 1) > dev->maxTemp) {
dev->maxTemp = i + 1;
for (j = 0; j <= i; j++)
dev->tempBuffer[j].maxLine =
dev->tempBuffer[j].line;
}
return dev->tempBuffer[i].buffer;
}
}
T(YAFFS_TRACE_BUFFERS,
(TSTR("Out of temp buffers at line %d, other held by lines:"),
lineNo));
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line));
}
T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR)));
/*
* If we got here then we have to allocate an unmanaged one
* This is not good.
*/
dev->unmanagedTempAllocations++;
return YMALLOC(dev->nDataBytesPerChunk);
}
static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer,
int lineNo)
{
int i;
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
if (dev->tempBuffer[i].buffer == buffer) {
dev->tempBuffer[i].line = 0;
return;
}
}
if (buffer) {
/* assume it is an unmanaged one. */
T(YAFFS_TRACE_BUFFERS,
(TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR),
lineNo));
YFREE(buffer);
dev->unmanagedTempDeallocations++;
}
}
/*
* Determine if we have a managed buffer.
*/
int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer)
{
int i;
for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) {
if (dev->tempBuffer[i].buffer == buffer)
return 1;
}
for (i = 0; i < dev->nShortOpCaches; i++) {
if( dev->srCache[i].data == buffer )
return 1;
}
if (buffer == dev->checkpointBuffer)
return 1;
T(YAFFS_TRACE_ALWAYS,
(TSTR("yaffs: unmaged buffer detected.\n" TENDSTR)));
return 0;
}
/*
* Chunk bitmap manipulations
*/
static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk)
{
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->internalStartBlock));
}
static Y_INLINE void yaffs_Veri