package yaffs2.port;
import yaffs2.utils.*;
import yaffs2.utils.factory.PrimitiveWrapperFactory;
public class yaffs_guts_C
{
/*
* 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.
*/
static final String yaffs_guts_c_version =
"$Id: yaffs_guts_C.java,v 1.6 2007/09/24 13:30:33 peter.hilber Exp $";
/*#include "yportenv.h"
#include "yaffsinterface.h"
#include "yaffs_guts.h"
#include "yaffs_tagsvalidity.h"
#include "yaffs_tagscompat.h"
#ifndef CONFIG_YAFFS_OWN_SORT
#include "yaffs_qsort.h"
#endif
#include "yaffs_nand.h"
#include "yaffs_checkptrw.h"
#include "yaffs_nand.h"
#include "yaffs_packedtags2.h"*/
/*#ifdef CONFIG_YAFFS_WINCE
void yfsd_LockYAFFS(BOOL fsLockOnly);
void yfsd_UnlockYAFFS(BOOL fsLockOnly);
#endif*/
static final int 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);
// #ifdef YAFFS_PARANOID
// static int yaffs_CheckFileSanity(yaffs_Object * in);
// #else
// #define yaffs_CheckFileSanity(in) // PORT
static void yaffs_CheckFileSanity(yaffs_Object 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);
/* Function to calculate chunk and offset */
static void yaffs_AddrToChunk(yaffs_Device dev, int addr, IntegerPointer chunk, IntegerPointer offset)
{
if(dev.subField2.chunkShift != 0){
/* Easy-peasy power of 2 case */
chunk.dereferenced = /*(__u32)*/(addr >>> dev.subField2.chunkShift);
offset.dereferenced = /*(__u32)*/(addr & dev.subField2.chunkMask);
}
else if(dev.subField2.crumbsPerChunk != 0)
{
/* Case where we're using "crumbs" */
offset.dereferenced = /*(__u32)*/(addr & dev.subField2.crumbMask);
addr >>>= dev.subField2.crumbShift;
chunk.dereferenced = (/*(__u32)*/addr)/dev.subField2.crumbsPerChunk;
offset.dereferenced += ((addr - (chunk.dereferenced * dev.subField2.crumbsPerChunk)) << dev.subField2.crumbShift);
}
else
yaffs2.utils.Globals.portConfiguration.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 int ShiftsGE(/*__u32*/ int x)
{
int extraBits;
int nShifts;
nShifts = extraBits = 0;
while(Utils.intAsUnsignedInt(x)>1){
if((x & 1) != 0)extraBits++;
x>>>=1;
nShifts++;
}
if(extraBits != 0)
nShifts++;
return nShifts;
}
/* Function to return the number of shifts to get a 1 in bit 0
*/
static int ShiftDiv(int x)
{
int nShifts;
nShifts = 0;
if(!(x != 0)) return 0;
while( !((x&1) != 0)){
x>>>=1;
nShifts++;
}
return nShifts;
}
/*
* Temporary buffer manipulations.
*/
static byte[] yaffs_GetTempBuffer(yaffs_Device dev, int lineNo)
{
int i, j;
for (i = 0; i < Guts_H.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;
}
}
yportenv.T(yportenv.YAFFS_TRACE_BUFFERS,
("Out of temp buffers at line %d, other held by lines:"),
PrimitiveWrapperFactory.get(lineNo));
for (i = 0; i < Guts_H.YAFFS_N_TEMP_BUFFERS; i++) {
yportenv.T(yportenv.YAFFS_TRACE_BUFFERS, (" %d "), PrimitiveWrapperFactory.get(dev.tempBuffer[i].line));
}
yportenv.T(yportenv.YAFFS_TRACE_BUFFERS, ((" " + ydirectenv.TENDSTR)));
/*
* If we got here then we have to allocate an unmanaged one
* This is not good.
*/
dev.unmanagedTempAllocations++;
return ydirectenv.YMALLOC(dev.subField1.nDataBytesPerChunk);
}
static void yaffs_ReleaseTempBuffer(yaffs_Device dev, byte[] buffer,
int lineNo)
{
int i;
for (i = 0; i < Guts_H.YAFFS_N_TEMP_BUFFERS; i++) {
if (dev.tempBuffer[i].buffer == buffer) {
dev.tempBuffer[i].line = 0;
return;
}
}
if (buffer != null) {
/* assume it is an unmanaged one. */
yportenv.T(yportenv.YAFFS_TRACE_BUFFERS,
("Releasing unmanaged temp buffer in line %d" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(lineNo));
ydirectenv.YFREE(buffer);
dev.unmanagedTempDeallocations++;
}
}
/*
* Determine if we have a managed buffer.
*/
static boolean yaffs_IsManagedTempBuffer(yaffs_Device dev, byte[] buffer)
{
int i;
for (i = 0; i < Guts_H.YAFFS_N_TEMP_BUFFERS; i++) {
if (dev.tempBuffer[i].buffer == buffer)
return true;
}
for (i = 0; i < dev.subField1.nShortOpCaches; i++) {
if( dev.srCache[i].data == buffer )
return true;
}
if (buffer == dev.subField2.checkpointBuffer)
return true;
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("yaffs: unmaged buffer detected.\n" + ydirectenv.TENDSTR));
return false;
}
/*
* Chunk bitmap manipulations
*/
static /*Y_INLINE*/ /*byte[]*/ ArrayPointer yaffs_BlockBits(yaffs_Device dev, int blk)
{
if (blk < dev.subField2.internalStartBlock || blk > dev.subField2.internalEndBlock) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("**>> yaffs: BlockBits block %d is not valid" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(blk));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
return new ArrayPointer(dev.subField2.chunkBits, (dev.subField3.chunkBitmapStride * (blk - dev.subField2.internalStartBlock)));
}
static /*Y_INLINE*/ void yaffs_ClearChunkBits(yaffs_Device dev, int blk)
{
ArrayPointer blkBits = yaffs_BlockBits(dev, blk);
Unix.memset(blkBits.array, blkBits.index, (byte)0, dev.subField3.chunkBitmapStride);
}
static /*Y_INLINE*/ void yaffs_ClearChunkBit(yaffs_Device dev, int blk, int chunk)
{
ArrayPointer blkBits = yaffs_BlockBits(dev, blk);
byte _buf = blkBits.get(chunk / 8);
_buf &= ~(1 << (chunk & 7));
blkBits.set(chunk / 8, _buf);
}
static /*Y_INLINE*/ void yaffs_SetChunkBit(yaffs_Device dev, int blk, int chunk)
{
ArrayPointer blkBits = yaffs_BlockBits(dev, blk);
byte _buf = blkBits.get(chunk / 8);
_buf |= (1 << (chunk & 7));
blkBits.set(chunk / 8, _buf);
}
static /*Y_INLINE*/ boolean yaffs_CheckChunkBit(yaffs_Device dev, int blk, int chunk)
{
ArrayPointer blkBits = yaffs_BlockBits(dev, blk);
return ((blkBits.get(chunk / 8) & (1 << (chunk & 7))) != 0) ? true : false;
}
static /*Y_INLINE*/ boolean yaffs_StillSomeChunkBits(yaffs_Device dev, int blk)
{
ArrayPointer blkBits = yaffs_BlockBits(dev, blk);
int i;
for (i = 0; i < dev.subField3.chunkBitmapStride; i++) {
if (blkBits.get() != 0)
return true;
blkBits.increment();
}
return false;
}
/*
* Simple hash function. Needs to have a reasonable spread
*/
static /*Y_INLINE*/ int yaffs_HashFunction(int n)
{
n = Math.abs(n);
return (n % Guts_H.YAFFS_NOBJECT_BUCKETS);
}
/*
* Access functions to useful fake objects
*/
static yaffs_Object yaffs_Root(yaffs_Device dev)
{
return dev.rootDir;
}
static yaffs_Object yaffs_LostNFound(yaffs_Device dev)
{
return dev.lostNFoundDir;
}
/*
* Erased NAND checking functions
*/
static boolean yaffs_CheckFF(byte[] buffer, int bufferIndex, int nBytes)
{
/* Horrible, slow implementation */
int i = 0;
while ((nBytes--) != 0) {
if (Utils.byteAsUnsignedByte(buffer[bufferIndex+i]) != 0xFF)
return false;
i++;
}
return true;
}
static boolean yaffs_CheckChunkErased(yaffs_Device dev,
int chunkInNAND)
{
boolean retval = Guts_H.YAFFS_OK;
byte[] data = yaffs_GetTempBuffer(dev, 1 /*Utils.__LINE__()*/);
final int dataIndex = 0;
yaffs_ExtendedTags tags = new yaffs_ExtendedTags();
boolean result;
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, dataIndex, tags);
if(tags.eccResult > Guts_H.YAFFS_ECC_RESULT_NO_ERROR)
retval = Guts_H.YAFFS_FAIL;
if (!yaffs_CheckFF(data, dataIndex, dev.subField1.nDataBytesPerChunk) || tags.chunkUsed) {
yportenv.T(yportenv.YAFFS_TRACE_NANDACCESS,
("Chunk %d not erased" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(chunkInNAND));
retval = Guts_H.YAFFS_FAIL;
}
yaffs_ReleaseTempBuffer(dev, data, 2 /*Utils.__LINE__()*/);
return retval;
}
static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device dev,
byte[] data, int dataIndex,
yaffs_ExtendedTags tags,
boolean useReserve)
{
int chunk;
boolean writeOk = false;
boolean erasedOk = true;
int attempts = 0;
yaffs_BlockInfo bi;
yaffs_InvalidateCheckpoint(dev);
do {
yaffs_BlockInfoPointer biPointer = new yaffs_BlockInfoPointer();
chunk = yaffs_AllocateChunk(dev, useReserve, biPointer);
bi = biPointer.dereferenced;
if (chunk >= 0) {
/* First check this chunk is erased, if it needs checking.
* The checking policy (unless forced always on) is as follows:
* Check the first page we try to write in a block.
* - If the check passes then we don't need to check any more.
* - If the check fails, we check again...
* If the block has been erased, we don't need to check.
*
* However, if the block has been prioritised for gc, then
* we think there might be something odd about this block
* and stop using it.
*
* Rationale:
* We should only ever see chunks that have not been erased
* if there was a partially written 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.
*/
if(bi.gcPrioritise()){
yaffs_DeleteChunk(dev, chunk, true, 3 /*Utils.__LINE__()*/);
} else {
/*#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED
bi->skipErasedCheck = 0;
#endif*/
if(!bi.skipErasedCheck()){
erasedOk = yaffs_CheckChunkErased(dev, chunk);
if(erasedOk && !bi.gcPrioritise())
bi.setSkipErasedCheck(true);
}
if (!erasedOk) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("**>> yaffs chunk %d was not erased"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(chunk));
} else {
writeOk =
yaffs_nand_C.yaffs_WriteChunkWithTagsToNAND(dev, chunk,
data, dataIndex, tags);
}
attempts++;
if (writeOk) {
/*
* Copy the data into the robustification buffer.
* NB We do this at the end to prevent duplicates in the case of a write error.
* Todo
*/
yaffs_HandleWriteChunkOk(dev, chunk, data, dataIndex, tags);
} else {
/* The erased check or write failed */
yaffs_HandleWriteChunkError(dev, chunk, erasedOk);
}
}
}
} while (chunk >= 0 && !writeOk);
if (attempts > 1) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("**>> yaffs write required %d attempts" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(attempts));
dev.subField3.nRetriedWrites += (attempts - 1);
}
return chunk;
}
/*
* Block retiring for handling a broken block.
*/
static void yaffs_RetireBlock(yaffs_Device dev, int blockInNAND)
{
yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev, blockInNAND);
yaffs_InvalidateCheckpoint(dev);
yaffs_nand_C.yaffs_MarkBlockBad(dev, blockInNAND);
bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_DEAD);
bi.setGcPrioritise(false);
bi.setNeedsRetiring(false);
dev.subField3.nRetiredBlocks++;
}
/*
* Functions for robustisizing TODO
*
*/
static void yaffs_HandleWriteChunkOk(yaffs_Device dev, int chunkInNAND,
byte[] data, int dataIndex,
yaffs_ExtendedTags tags)
{
}
static void yaffs_HandleUpdateChunk(yaffs_Device dev, int chunkInNAND,
yaffs_ExtendedTags tags)
{
}
static void yaffs_HandleChunkError(yaffs_Device dev, yaffs_BlockInfo bi)
{
if(!bi.gcPrioritise()){
bi.setGcPrioritise(true);
dev.hasPendingPrioritisedGCs = true;
bi.setChunkErrorStrikes(bi.chunkErrorStrikes() + 1);
if(bi.chunkErrorStrikes() > 3){
bi.setNeedsRetiring(true); /* Too many stikes, so retire this */
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS, ("yaffs: Block struck out" + ydirectenv.TENDSTR));
}
}
}
static void yaffs_ReportOddballBlocks(yaffs_Device dev)
{
int i;
for(i = dev.subField2.internalStartBlock; i <= dev.subField2.internalEndBlock && ((yaffs2.utils.Globals.yaffs_traceMask & yportenv.YAFFS_TRACE_BAD_BLOCKS) != 0); i++){
yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev,i);
if(bi.needsRetiring() || bi.gcPrioritise())
yportenv.T(yportenv.YAFFS_TRACE_BAD_BLOCKS, ("yaffs block %d%s%s" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(i),
PrimitiveWrapperFactory.get(bi.needsRetiring() ? " needs retiring" : ""), // XXX hope no gc
PrimitiveWrapperFactory.get(bi.gcPrioritise() ? " gc prioritised" : ""));
}
}
static void yaffs_HandleWriteChunkError(yaffs_Device dev, int chunkInNAND, boolean erasedOk)
{
int blockInNAND = chunkInNAND / dev.subField1.nChunksPerBlock;
yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev, blockInNAND);
yaffs_HandleChunkError(dev,bi);
if(erasedOk ) {
/* Was an actual write failure, so mark the block for retirement */
bi.setNeedsRetiring(true);
yportenv.T(yportenv.YAFFS_TRACE_ERROR | yportenv.YAFFS_TRACE_BAD_BLOCKS,
("**>> Block %d needs retiring" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blockInNAND));
}
/* Delete the chunk */
yaffs_DeleteChunk(dev, chunkInNAND, true, 4 /*Utils.__LINE__()*/);
}
/*---------------- Name handling functions ------------*/
/**
* @return _u16
*/
static short yaffs_CalcNameSum(byte[] name, int nameIndex)
{
short sum = 0;
int i = 1;
byte[] bname = name;
int bnameIndex = nameIndex;
if (bname != null) {
while (bname[bnameIndex] != 0 && (i <= Guts_H.YAFFS_MAX_NAME_LENGTH)) {
/*#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
sum += yaffs_toupper(*bname) * i;
#else*/
sum += Utils.byteAsUnsignedByte(bname[bnameIndex]) * i;
/*#endif*/
i++;
bnameIndex++;
}
}
return sum;
}
static void yaffs_SetObjectName(yaffs_Object obj, byte[] name, int nameIndex)
{
/*#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM*/
if ((name != null) && ydirectenv.yaffs_strlen(name, nameIndex) <= Guts_H.YAFFS_SHORT_NAME_LENGTH) {
ydirectenv.yaffs_strcpy(obj.shortName, 0, name, nameIndex);
} else {
obj.shortName[0] = ((byte)0);
}
/*#endif*/
obj.sum = yaffs_CalcNameSum(name, nameIndex);
}
/*-------------------- TNODES -------------------
* List of spare tnodes
* The list is hooked together using the first pointer
* in the tnode.
*/
/* yaffs_CreateTnodes creates a bunch more tnodes and
* adds them to the tnode free list.
* Don't use this function directly
*/
static boolean yaffs_CreateTnodes(yaffs_Device dev, int nTnodes)
{
int i;
int tnodeSize;
yaffs_Tnode[] newTnodes;
//byte[] mem;
yaffs_Tnode curr;
yaffs_Tnode next;
yaffs_TnodeList tnl;
if (nTnodes < 1)
return Guts_H.YAFFS_OK;
/* Calculate the tnode size in bytes for variable width tnode support.
* Must be a multiple of 32-bits */
tnodeSize = (dev.subField2.tnodeWidth * Guts_H.YAFFS_NTNODES_LEVEL0)/8;
/* make these things */
newTnodes = /*ydirectenv.YMALLOC(nTnodes * tnodeSize)*/ ydirectenv.YMALLOC_TNODE(nTnodes);
//mem = (__u8 *)newTnodes;
if (newTnodes == null) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
(("yaffs: Could not allocate Tnodes" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
/* Hook them into the free list */
/*#if 0
for (i = 0; i < nTnodes - 1; i++) {
newTnodes[i].internal[0] = &newTnodes[i + 1];
#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
#endif
}
newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
#endif
dev->freeTnodes = newTnodes;
#else*/
/* New hookup for wide tnodes */
for(i = 0; i < nTnodes -1; i++) {
curr = /*(yaffs_Tnode *) &mem[i * tnodeSize]*/ newTnodes[i];
next = /*(yaffs_Tnode *) &mem[(i+1) * tnodeSize]*/ newTnodes[i+1];
curr.internal[0] = next;
}
curr = /*(yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize]*/ newTnodes[nTnodes - 1];
curr.internal[0] = dev.subField3.freeTnodes;
dev.subField3.freeTnodes = /*(yaffs_Tnode *)mem*/ newTnodes[0];
/*#endif*/
dev.subField3.nFreeTnodes += nTnodes;
dev.subField3.nTnodesCreated += nTnodes;
/* Now add this bunch of tnodes to a list for freeing up.
* NB If we can't add this to the management list it isn't fatal
* but it just means we can't free this bunch of tnodes later.
*/
tnl = /*ydirectenv.YMALLOC(sizeof(yaffs_TnodeList))*/ new yaffs_TnodeList();
if (tnl == null) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
(
("yaffs: Could not add tnodes to management list" + ydirectenv.TENDSTR)));
} else {
tnl.tnodes = newTnodes;
tnl.next = dev.subField3.allocatedTnodeList;
dev.subField3.allocatedTnodeList = tnl;
}
yportenv.T(yportenv.YAFFS_TRACE_ALLOCATE, (("yaffs: Tnodes added" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_OK;
}
/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */
static yaffs_Tnode yaffs_GetTnodeRaw(yaffs_Device dev)
{
yaffs_Tnode tn = null;
/* If there are none left make more */
if (dev.subField3.freeTnodes == null) {
yaffs_CreateTnodes(dev, Guts_H.YAFFS_ALLOCATION_NTNODES);
}
if (dev.subField3.freeTnodes != null) {
tn = dev.subField3.freeTnodes;
// #ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
// if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) {
// /* Hoosterman, this thing looks like it isn't in the list */
// yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
// (("yaffs: Tnode list bug 1" ydirectenv.TENDSTR)));
// }
// #endif
dev.subField3.freeTnodes = dev.subField3.freeTnodes.internal[0];
dev.subField3.nFreeTnodes--;
}
return tn;
}
static yaffs_Tnode yaffs_GetTnode(yaffs_Device dev)
{
yaffs_Tnode tn = yaffs_GetTnodeRaw(dev);
if(tn != null)
Unix.memset(tn/*, 0, (dev.tnodeWidth * Guts_H.YAFFS_NTNODES_LEVEL0)/8*/);
return tn;
}
/* FreeTnode frees up a tnode and puts it back on the free list */
static void yaffs_FreeTnode(yaffs_Device dev, yaffs_Tnode tn)
{
if (tn != null) {
// #ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
// if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) {
// /* Hoosterman, this thing looks like it is already in the list */
// yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
// (("yaffs: Tnode list bug 2" ydirectenv.TENDSTR)));
// }
// tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
// #endif
tn.internal[0] = dev.subField3.freeTnodes;
dev.subField3.freeTnodes = tn;
dev.subField3.nFreeTnodes++;
}
}
// XXX does this work? only needed for aborted checkpoint loading, and deinitialising
static void yaffs_DeinitialiseTnodes(yaffs_Device dev)
{
/* Free the list of allocated tnodes */
yaffs_TnodeList tmp;
while (dev.subField3.allocatedTnodeList != null) {
tmp = dev.subField3.allocatedTnodeList.next;
// XXX has it a chance to work?
// XXX or try another strategy?
/*ydirectenv.YFREE(dev->allocatedTnodeList->tnodes)*/ dev.subField3.allocatedTnodeList.tnodes = null;
/*ydirectenv.YFREE(dev->allocatedTnodeList)*/ dev.subField3.allocatedTnodeList = null;
dev.subField3.allocatedTnodeList = tmp;
}
dev.subField3.freeTnodes = null;
dev.subField3.nFreeTnodes = 0;
}
static void yaffs_InitialiseTnodes(yaffs_Device dev)
{
dev.subField3.allocatedTnodeList = null;
dev.subField3.freeTnodes = null;
dev.subField3.nFreeTnodes = 0;
dev.subField3.nTnodesCreated = 0;
}
static void yaffs_PutLevel0Tnode(yaffs_Device dev, yaffs_Tnode tn, int pos, int val)
{
/*__u32 map = (__u32 *)tn*/;
int bitInMap;
int bitInWord;
int wordInMap;
int mask;
pos &= Guts_H.YAFFS_TNODES_LEVEL0_MASK;
val >>>= dev.subField1.chunkGroupBits;
bitInMap = pos * dev.subField2.tnodeWidth;
wordInMap = bitInMap /32;
bitInWord = bitInMap & (32 -1);
mask = dev.subField2.tnodeMask << bitInWord;
tn.andLevel0AsInt(wordInMap, ~mask);
tn.orLevel0AsInt(wordInMap, (mask & (val << bitInWord)));
if(dev.subField2.tnodeWidth > (32-bitInWord)) {
bitInWord = (32 - bitInWord);
wordInMap++;;
mask = dev.subField2.tnodeMask >>> (/*dev->tnodeWidth -*/ bitInWord);
tn.andLevel0AsInt(wordInMap, ~mask);
tn.orLevel0AsInt(wordInMap, mask & (val >>> bitInWord));
}
// FIXME
yportenv.T(yportenv.PORT_TRACE_TNODE, "PutLevel0Tnode: pos %d val %d map[wordInMap]: %d\n", PrimitiveWrapperFactory.get(pos), PrimitiveWrapperFactory.get(val), PrimitiveWrapperFactory.get(tn.level0AsInt(wordInMap)));
}
static int yaffs_GetChunkGroupBase(yaffs_Device dev, yaffs_Tnode tn, int pos)
{
/*__u32 *map = (__u32 *)tn;*/
int bitInMap;
int bitInWord;
int wordInMap;
int val;
pos &= Guts_H.YAFFS_TNODES_LEVEL0_MASK;
bitInMap = pos * dev.subField2.tnodeWidth;
wordInMap = bitInMap /32;
bitInWord = bitInMap & (32 -1);
val = tn.level0AsInt(wordInMap) >>> bitInWord;
if(dev.subField2.tnodeWidth > (32-bitInWord)) {
bitInWord = (32 - bitInWord);
wordInMap++;;
val |= (tn.level0AsInt(wordInMap) << bitInWord);
}
val &= dev.subField2.tnodeMask;
val <<= dev.subField1.chunkGroupBits;
return val;
}
/* ------------------- End of individual tnode manipulation -----------------*/
/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------
* The look up tree is represented by the top tnode and the number of topLevel
* in the tree. 0 means only the level 0 tnode is in the tree.
*/
/* FindLevel0Tnode finds the level 0 tnode, if one exists. */
static yaffs_Tnode yaffs_FindLevel0Tnode(yaffs_Device dev,
yaffs_FileStructure fStruct,
int chunkId)
{
yaffs_Tnode tn = fStruct.top;
int i;
int requiredTallness;
int level = fStruct.topLevel;
/* Check sane level and chunk Id */
if (level < 0 || level > Guts_H.YAFFS_TNODES_MAX_LEVEL) {
return null;
}
if (chunkId > Guts_H.YAFFS_MAX_CHUNK_ID) {
return null;
}
/* First check we're tall enough (ie enough topLevel) */
i = chunkId >>> Guts_H.YAFFS_TNODES_LEVEL0_BITS;
requiredTallness = 0;
while (i != 0) {
i >>>= Guts_H.YAFFS_TNODES_INTERNAL_BITS;
requiredTallness++;
}
if (requiredTallness > fStruct.topLevel) {
/* Not tall enough, so we can't find it, return NULL. */
return null;
}
/* Traverse down to level 0 */
while (level > 0 && tn != null) {
tn = tn.
internal[(chunkId >>>
( Guts_H.YAFFS_TNODES_LEVEL0_BITS +
(level - 1) *
Guts_H.YAFFS_TNODES_INTERNAL_BITS)
) &
Guts_H.YAFFS_TNODES_INTERNAL_MASK];
level--;
}
return tn;
}
/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
* This happens in two steps:
* 1. If the tree isn't tall enough, then make it taller.
* 2. Scan down the tree towards the level 0 tnode adding tnodes if required.
*
* Used when modifying the tree.
*
* If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will
* be plugged into the ttree.
*/
static yaffs_Tnode yaffs_AddOrFindLevel0Tnode(yaffs_Device dev,
yaffs_FileStructure fStruct,
int chunkId,
yaffs_Tnode passedTn)
{
int requiredTallness;
int i;
int l;
yaffs_Tnode tn;
int x;
/* Check sane level and page Id */
if (fStruct.topLevel < 0 || fStruct.topLevel > Guts_H.YAFFS_TNODES_MAX_LEVEL) {
return null;
}
if (Utils.intAsUnsignedInt(chunkId) > Guts_H.YAFFS_MAX_CHUNK_ID) {
return null;
}
/* First check we're tall enough (ie enough topLevel) */
x = chunkId >>> Guts_H.YAFFS_TNODES_LEVEL0_BITS;
requiredTallness = 0;
while (x != 0) {
x >>>= Guts_H.YAFFS_TNODES_INTERNAL_BITS;
requiredTallness++;
}
if (requiredTallness > fStruct.topLevel) {
/* Not tall enough,gotta make the tree taller */
for (i = fStruct.topLevel; i < requiredTallness; i++) {
tn = yaffs_GetTnode(dev);
if (tn != null) {
tn.internal[0] = fStruct.top;
fStruct.top = tn;
} else {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
(("yaffs: no more tnodes" + ydirectenv.TENDSTR)));
}
}
// FIXME
if (requiredTallness > 0)
yportenv.T(yportenv.PORT_TRACE_TALLNESS, "Required tallness: %d\n", PrimitiveWrapperFactory.get(requiredTallness));
fStruct.topLevel = requiredTallness;
}
/* Traverse down to level 0, adding anything we need */
l = fStruct.topLevel;
tn = fStruct.top;
if(l > 0) {
while (l > 0 && tn != null) {
x = (chunkId >>>
( Guts_H.YAFFS_TNODES_LEVEL0_BITS +
(l - 1) * Guts_H.YAFFS_TNODES_INTERNAL_BITS)) &
Guts_H.YAFFS_TNODES_INTERNAL_MASK;
if((l>1) && (tn.internal[x] == null)){
/* Add missing non-level-zero tnode */
tn.internal[x] = yaffs_GetTnode(dev);
} else if(l == 1) {
/* Looking from level 1 at level 0 */
if (passedTn != null) {
/* If we already have one, then release it.*/
if(tn.internal[x] != null)
yaffs_FreeTnode(dev,tn.internal[x]);
tn.internal[x] = passedTn;
} else if(tn.internal[x] == null) {
/* Don't have one, none passed in */
tn.internal[x] = yaffs_GetTnode(dev);
}
}
tn = tn.internal[x];
l--;
}
} else {
/* top is level 0 */
if(passedTn != null) {
Unix.memcpy(tn,passedTn/*,(dev.tnodeWidth * Guts_H.YAFFS_NTNODES_LEVEL0)/8*/); // XXX only copy level0?
yaffs_FreeTnode(dev,passedTn);
}
}
return tn;
}
static int yaffs_FindChunkInGroup(yaffs_Device dev, int theChunk,
yaffs_ExtendedTags tags, int objectId,
int chunkInInode)
{
int j;
for (j = 0; theChunk != 0 && j < dev.subField1.chunkGroupSize; j++) {
if (yaffs_CheckChunkBit
(dev, theChunk / dev.subField1.nChunksPerBlock,
theChunk % dev.subField1.nChunksPerBlock)) {
yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, null, 0,
tags);
if (yaffs_TagsMatch(tags, objectId, chunkInInode)) {
/* found it; */
return theChunk;
}
}
theChunk++;
}
return -1;
}
/* DeleteWorker scans backwards through the tnode tree and deletes all the
* chunks and tnodes in the file
* Returns 1 if the tree was deleted.
* Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
*/
// XXX recursive
static boolean yaffs_DeleteWorker(yaffs_Object in, yaffs_Tnode tn, int level,
int chunkOffset, IntegerPointer limit)
{
int i;
int chunkInInode;
int theChunk;
yaffs_ExtendedTags tags = new yaffs_ExtendedTags();
int foundChunk;
yaffs_Device dev = in.myDev;
boolean allDone = true;
if (tn != null) {
if (level > 0) {
for (i = Guts_H.YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0;
i--) {
if (tn.internal[i] != null) {
if (limit != null && (limit.dereferenced) < 0) {
allDone = false;
} else {
allDone =
yaffs_DeleteWorker(in,
tn.
internal
[i],
level -
1,
(chunkOffset
<<
Guts_H.YAFFS_TNODES_INTERNAL_BITS)
+ i,
limit);
}
if (allDone) {
yaffs_FreeTnode(dev,
tn.
internal[i]);
tn.internal[i] = null;
}
}
}
return (allDone) ? true : false;
} else if (level == 0) {
int hitLimit = 0;
for (i = Guts_H.YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && (hitLimit == 0);
i--) {
theChunk = yaffs_GetChunkGroupBase(dev,tn,i);
if (theChunk != 0) {
chunkInInode =
(chunkOffset <<
Guts_H.YAFFS_TNODES_LEVEL0_BITS) + i;
foundChunk =
yaffs_FindChunkInGroup(dev,
theChunk,
tags,
in.objectId,
chunkInInode);
if (foundChunk > 0) {
yaffs_DeleteChunk(dev,
foundChunk, true,
5 /*Utils.__LINE__()*/);
in.nDataChunks--;
if (limit != null) {
limit.dereferenced = limit.dereferenced - 1;
if (limit.dereferenced <= 0) {
hitLimit = 1;
}
}
}
yaffs_PutLevel0Tnode(dev,tn,i,0);
}
}
return (i < 0) ? true : false;
}
}
return true;
}
static void yaffs_SoftDeleteChunk(yaffs_Device dev, int chunk)
{
yaffs_BlockInfo theBlock;
yportenv.T(yportenv.YAFFS_TRACE_DELETION, ("soft delete chunk %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(chunk));
theBlock = Guts_H.yaffs_GetBlockInfo(dev, chunk / dev.subField1.nChunksPerBlock);
if (theBlock != null) {
theBlock.setSoftDeletions(theBlock.softDeletions()+1);
dev.subField3.nFreeChunks++;
}
}
/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
* All soft deleting does is increment the block's softdelete count and pulls the chunk out
* of the tnode.
* Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
*/
// XXX recursive
static boolean yaffs_SoftDeleteWorker(yaffs_Object in, yaffs_Tnode tn,
int level, int chunkOffset)
{
int i;
int theChunk;
boolean allDone = true;
yaffs_Device dev = in.myDev;
if (tn != null) {
if (level > 0) {
for (i = Guts_H.YAFFS_NTNODES_INTERNAL - 1; (allDone) && i >= 0;
i--) {
if (tn.internal[i] != null) {
allDone =
yaffs_SoftDeleteWorker(in,
tn.
internal[i],
level - 1,
(chunkOffset
<<
Guts_H.YAFFS_TNODES_INTERNAL_BITS)
+ i);
if (allDone) {
yaffs_FreeTnode(dev,
tn.
internal[i]);
tn.internal[i] = null;
} else {
/* Hoosterman... how could this happen? */
}
}
}
return (allDone) ? true : false;
} else if (level == 0) {
for (i = Guts_H.YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) {
theChunk = yaffs_GetChunkGroupBase(dev,tn,i);
if (theChunk != 0) {
/* Note this does not find the real chunk, only the chunk group.
* We make an assumption that a chunk group is not larger than
* a block.
*/
yaffs_SoftDeleteChunk(dev, theChunk);
yaffs_PutLevel0Tnode(dev,tn,i,0);
}
}
return true;
}
}
return true;
}
static void yaffs_SoftDeleteFile(yaffs_Object obj)
{
if (obj.sub.deleted &&
obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE && !obj.sub.softDeleted) {
if (obj.nDataChunks <= 0) {
/* Empty file with no duplicate object headers, just delete it immediately */
yaffs_FreeTnode(obj.myDev,
obj.variant.fileVariant().top);
obj.variant.fileVariant().top = null;
yportenv.T(yportenv.YAFFS_TRACE_TRACING,
("yaffs: Deleting empty file %d" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(obj.objectId));
yaffs_DoGenericObjectDeletion(obj);
} else {
yaffs_SoftDeleteWorker(obj,
obj.variant.fileVariant().top,
obj.variant.fileVariant().
topLevel, 0);
obj.sub.softDeleted = true;
}
}
}
/* Pruning removes any part of the file structure tree that is beyond the
* bounds of the file (ie that does not point to chunks).
*
* A file should only get pruned when its size is reduced.
*
* Before pruning, the chunks must be pulled from the tree and the
* level 0 tnode entries must be zeroed out.
* Could also use this for file deletion, but that's probably better handled
* by a special case.
*/
static yaffs_Tnode yaffs_PruneWorker(yaffs_Device dev, yaffs_Tnode tn,
int level, boolean del0)
{
int i;
int hasData;
if (tn != null) {
hasData = 0;
for (i = 0; i < Guts_H.YAFFS_NTNODES_INTERNAL; i++) {
// assert (level > 0 ?
// Utils.getIntFromByteArray(tn.serialized, i*4) == 0 :
// tn.internal[i] == null);
if (tn.internal[i] != null && level > 0) {
tn.internal[i] = // XXX recursive call
yaffs_PruneWorker(dev, tn.internal[i],
level - 1,
(i == 0) ? del0 : true);
}
if ((tn.internal[i] != null) || // PORT union
(Utils.getIntFromByteArray(tn.serialized, tn.offset+i*4) != 0)) {
hasData++;
}
}
if (hasData == 0 && del0) {
/* Free and return NULL */
yaffs_FreeTnode(dev, tn);
tn = null;
}
}
return tn;
}
static boolean yaffs_PruneFileStructure(yaffs_Device dev,
yaffs_FileStructure fStruct)
{
int i;
int hasData;
boolean done = false;
yaffs_Tnode tn;
if (fStruct.topLevel > 0) {
fStruct.top =
yaffs_PruneWorker(dev, fStruct.top, fStruct.topLevel, false);
/* Now we have a tree with all the non-zero branches NULL but the height
* is the same as it was.
* Let's see if we can trim internal tnodes to shorten the tree.
* We can do this if only the 0th element in the tnode is in use
* (ie all the non-zero are NULL)
*/
while (fStruct.topLevel != 0 && !done) {
tn = fStruct.top;
hasData = 0;
for (i = 1; i < Guts_H.YAFFS_NTNODES_INTERNAL; i++) {
if (tn.internal[i] != null) {
hasData++;
}
}
if (hasData == 0) {
fStruct.top = tn.internal[0];
fStruct.topLevel--;
// FIXME
yportenv.T(yportenv.PORT_TRACE_TOPLEVEL, "Reducing topLevel: %d\n", PrimitiveWrapperFactory.get(fStruct.topLevel));
yaffs_FreeTnode(dev, tn);
} else {
done = true;
}
}
}
return Guts_H.YAFFS_OK;
}
/*-------------------- End of File Structure functions.-------------------*/
/* yaffs_CreateFreeObjects creates a bunch more objects and
* adds them to the object free list.
*/
static boolean yaffs_CreateFreeObjects(yaffs_Device dev, int nObjects)
{
int i;
yaffs_Object[] newObjects;
yaffs_ObjectList list;
if (nObjects < 1)
return Guts_H.YAFFS_OK;
/* make these things */
newObjects = ydirectenv.YMALLOC_OBJECT(nObjects/* * sizeof(yaffs_Object)*/ );
if (!(newObjects != null)) {
yportenv.T(yportenv.YAFFS_TRACE_ALLOCATE,
(("yaffs: Could not allocate more objects" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
/* Hook them into the free list */
for (i = 0; i < nObjects - 1; i++) {
newObjects[i].siblings.next =
/*(list_head)*/ (newObjects[i + 1]);
}
newObjects[nObjects - 1].siblings.next = /*(void *)*/ dev.subField3.freeObjects;
dev.subField3.freeObjects = newObjects[0];
dev.subField3.nFreeObjects += nObjects;
dev.subField3.nObjectsCreated += nObjects;
/* Now add this bunch of Objects to a list for freeing up. */
list = /*ydirectenv.YMALLOC(sizeof(yaffs_ObjectList))*/ new yaffs_ObjectList();
if (!(list != null)) {
yportenv.T(yportenv.YAFFS_TRACE_ALLOCATE,
(("Could not add objects to management list" + ydirectenv.TENDSTR)));
} else {
list.objects = newObjects;
list.next = dev.subField3.allocatedObjectList;
dev.subField3.allocatedObjectList = list;
}
return Guts_H.YAFFS_OK;
}
/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */
static yaffs_Object yaffs_AllocateEmptyObject(yaffs_Device dev)
{
yaffs_Object tn = null;
/* If there are none left make more */
if (!(dev.subField3.freeObjects != null)) {
yaffs_CreateFreeObjects(dev, Guts_H.YAFFS_ALLOCATION_NOBJECTS);
}
if (dev.subField3.freeObjects != null) {
tn = dev.subField3.freeObjects;
dev.subField3.freeObjects =
(yaffs_Object) (dev.subField3.freeObjects.siblings.next);
dev.subField3.nFreeObjects--;
/* Now sweeten it up... */
Unix.memset(tn/*, 0, sizeof(yaffs_Object)*/ );
tn.myDev = dev;
tn.chunkId = -1;
tn.variantType = Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN;
devextras.INIT_LIST_HEAD((tn.hardLinks));
devextras.INIT_LIST_HEAD((tn.hashLink));
devextras.INIT_LIST_HEAD(tn.siblings);
/* Add it to the lost and found directory.
* NB Can't put root or lostNFound in lostNFound so
* check if lostNFound exists first
*/
if (dev.lostNFoundDir != null) {
yaffs_AddObjectToDirectory(dev.lostNFoundDir, tn);
}
}
return tn;
}
static yaffs_Object yaffs_CreateFakeDirectory(yaffs_Device dev, int number,
int mode)
{
yaffs_Object obj =
yaffs_CreateNewObject(dev, number, Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY);
if (obj != null) {
obj.sub.fake = true; /* it is fake so it has no NAND presence... */
obj.renameAllowed = false; /* ... and we're not allowed to rename it... */
obj.unlinkAllowed = false; /* ... or unlink it */
obj.sub.deleted = false;
obj.sub.unlinked = false;
obj.yst_mode = mode;
obj.myDev = dev;
obj.chunkId = 0; /* Not a valid chunk. */
}
return obj;
}
static void yaffs_UnhashObject(yaffs_Object tn)
{
int bucket;
yaffs_Device dev = tn.myDev;
/* If it is still linked into the bucket list, free from the list */
if (!devextras.list_empty(tn.hashLink)) {
devextras.list_del_init(tn.hashLink);
bucket = yaffs_HashFunction(tn.objectId);
dev.subField3.objectBucket[bucket].count--;
}
}
/* FreeObject frees up a Object and puts it back on the free list */
static void yaffs_FreeObject(yaffs_Object tn)
{
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. */
tn.siblings.next = /*(list_head)*/ (dev.subField3.freeObjects);
dev.subField3.freeObjects = tn;
dev.subField3.nFreeObjects++;
}
// #ifdef __KERNEL__
// void yaffs_HandleDeferedFree(yaffs_Object * obj)
// {
// if (obj.deferedFree) {
// yaffs_FreeObject(obj);
// }
// }
// #endif
static void yaffs_DeinitialiseObjects(yaffs_Device dev)
{
/* Free the list of allocated Objects */
yaffs_ObjectList tmp;
while (dev.subField3.allocatedObjectList != null) {
tmp = dev.subField3.allocatedObjectList.next;
ydirectenv.YFREE(dev.subField3.allocatedObjectList.objects);
ydirectenv.YFREE(dev.subField3.allocatedObjectList);
dev.subField3.allocatedObjectList = tmp;
}
dev.subField3.freeObjects = null;
dev.subField3.nFreeObjects = 0;
}
static void yaffs_InitialiseObjects(yaffs_Device dev)
{
int i;
dev.subField3.allocatedObjectList = null;
dev.subField3.freeObjects = null;
dev.subField3.nFreeObjects = 0;
for (i = 0; i < Guts_H.YAFFS_NOBJECT_BUCKETS; i++) {
devextras.INIT_LIST_HEAD(dev.subField3.objectBucket[i].list);
dev.subField3.objectBucket[i].count = 0;
}
}
static int _STATIC_LOCAL_yaffs_FindNiceObjectBucket_x = 0;
static int yaffs_FindNiceObjectBucket(yaffs_Device dev)
{
int i;
int l = 999;
int lowest = 999999;
/* First let's see if we can find one that's empty. */
for (i = 0; i < 10 && lowest > 0; i++) {
_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x++;
_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x %= Guts_H.YAFFS_NOBJECT_BUCKETS;
if (dev.subField3.objectBucket[_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x].count < lowest) {
lowest = dev.subField3.objectBucket[_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x].count;
l = _STATIC_LOCAL_yaffs_FindNiceObjectBucket_x;
}
}
/* If we didn't find an empty list, then try
* looking a bit further for a short one
*/
for (i = 0; i < 10 && lowest > 3; i++) {
_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x++;
_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x %= Guts_H.YAFFS_NOBJECT_BUCKETS;
if (dev.subField3.objectBucket[_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x].count < lowest) {
lowest = dev.subField3.objectBucket[_STATIC_LOCAL_yaffs_FindNiceObjectBucket_x].count;
l = _STATIC_LOCAL_yaffs_FindNiceObjectBucket_x;
}
}
return l;
}
static int yaffs_CreateNewObjectNumber(yaffs_Device dev)
{
int bucket = yaffs_FindNiceObjectBucket(dev);
/* Now find an object value that has not already been taken
* by scanning the list.
*/
boolean found = false;
list_head i;
/*__u32*/ int n = /*(__u32)*/ bucket; // XXX // ???
/* yaffs_CheckObjectHashSanity(); */
while (!found) {
found = true;
n += Guts_H.YAFFS_NOBJECT_BUCKETS;
if (true || dev.subField3.objectBucket[bucket].count > 0) {
// list_for_each(i, &dev->objectBucket[bucket].list) {
for (i = dev.subField3.objectBucket[bucket].list.next(); i != dev.subField3.objectBucket[bucket].list;
i = i.next())
{
/* If there is already one in the list */
if (i != null
&& ((yaffs_Object)i.list_entry).objectId == n) {
found = false;
}
}
}
}
return n;
}
static void yaffs_HashObject(yaffs_Object in)
{
int bucket = yaffs_HashFunction(in.objectId);
yaffs_Device dev = in.myDev;
devextras.list_add(in.hashLink, dev.subField3.objectBucket[bucket].list);
dev.subField3.objectBucket[bucket].count++;
}
static yaffs_Object yaffs_FindObjectByNumber(yaffs_Device dev, /*__u32*/ int number)
{
int bucket = yaffs_HashFunction(number);
list_head i;
yaffs_Object in;
// list_for_each(i, &dev.objectBucket[bucket].list) {
for (i = dev.subField3.objectBucket[bucket].list.next(); i != dev.subField3.objectBucket[bucket].list;
i = i.next()) {
/* Look if it is in the list */
if (i != null) {
in = /*list_entry(i, yaffs_Object, hashLink)*/ ((yaffs_Object)i.list_entry);
if (in.objectId == number) {
// #ifdef __KERNEL__
// /* Don't tell the VFS about this one if it is defered free */
// if (in.deferedFree)
// return null;
// #endif
return in;
}
}
}
return null;
}
static yaffs_Object yaffs_CreateNewObject(yaffs_Device dev, int number,
/*yaffs_ObjectType*/ int type)
{
yaffs_Object theObject;
if (number < 0) {
number = yaffs_CreateNewObjectNumber(dev);
}
theObject = yaffs_AllocateEmptyObject(dev);
if (theObject != null) {
theObject.sub.fake = false;
theObject.renameAllowed = true;
theObject.unlinkAllowed = true;
theObject.objectId = number;
yaffs_HashObject(theObject);
theObject.variantType = type;
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_WinFileTimeNow(theObject.win_atime);
// theObject.win_ctime[0] = theObject.win_mtime[0] =
// theObject.win_atime[0];
// theObject.win_ctime[1] = theObject.win_mtime[1] =
// theObject.win_atime[1];
// #else
theObject.yst_atime = theObject.yst_mtime =
theObject.yst_ctime = ydirectenv.Y_CURRENT_TIME();
// #endif
switch (type) { // XXX either allocate/get from pool the corresponding variant here, or create all possible variants on object creation
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
theObject.variant.fileVariant().fileSize = 0;
theObject.variant.fileVariant().scannedFileSize = 0;
theObject.variant.fileVariant().shrinkSize = 0xFFFFFFFF; /* max __u32 */
theObject.variant.fileVariant().topLevel = 0;
theObject.variant.fileVariant().top =
yaffs_GetTnode(dev);
break;
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
devextras.INIT_LIST_HEAD(theObject.variant.directoryVariant().
children);
break;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
/* No action required */
break;
case Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN:
/* todo this should not happen */
break;
}
}
return theObject;
}
static yaffs_Object yaffs_FindOrCreateObjectByNumber(yaffs_Device dev,
int number,
/*yaffs_ObjectType*/ int type)
{
yaffs_Object theObject = null;
if (number > 0) {
theObject = yaffs_FindObjectByNumber(dev, number);
}
if (theObject == null) {
theObject = yaffs_CreateNewObject(dev, number, type);
}
return theObject;
}
// XXX this is REALLY a bad function if we dont want to allocate memory
// XXX see if it is used after startup
// XXX only if it is done after startup see if we could replace it somehow
static byte[] yaffs_CloneString(byte[] str, int strIndex)
{
byte[] newStr = null;
if (str != null && str[0] != 0) {
newStr = ydirectenv.YMALLOC((ydirectenv.yaffs_strlen(str, strIndex) + 1)/* * sizeof(YCHAR)*/);
ydirectenv.yaffs_strcpy(newStr, 0, str, strIndex);
}
return newStr;
}
/*
* Mknod (create) a new object.
* equivalentObject only has meaning for a hard link;
* aliasString only has meaning for a sumlink.
* rdev only has meaning for devices (a subset of special objects)
*/
static yaffs_Object yaffs_MknodObject(/*yaffs_ObjectType*/ int type,
yaffs_Object parent,
byte[] name, int nameIndex,
int mode,
int uid,
int gid,
yaffs_Object equivalentObject,
byte[] aliasString, int aliasStringIndex,
int rdev)
{
yaffs_Object in;
yaffs_Device dev = parent.myDev;
/* Check if the entry exists. If it does then fail the call since we don't want a dup.*/
if (yaffs_FindObjectByName(parent, name, nameIndex) != null) {
return null;
}
in = yaffs_CreateNewObject(dev, -1, type);
if (in != null) {
in.chunkId = -1;
in.valid = true;
in.variantType = type;
in.yst_mode = mode;
// #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[1];
// #else
in.yst_atime = in.yst_mtime = in.yst_ctime = ydirectenv.Y_CURRENT_TIME();
in.yst_rdev = rdev;
in.yst_uid = uid;
in.yst_gid = gid;
// #endif
in.nDataChunks = 0;
yaffs_SetObjectName(in, name, nameIndex);
in.dirty = true;
yaffs_AddObjectToDirectory(parent, in);
in.myDev = parent.myDev;
switch (type) {
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
in.variant.symLinkVariant().alias =
yaffs_CloneString(aliasString, aliasStringIndex);
in.variant.symLinkVariant().aliasIndex = 0;
break;
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
in.variant.hardLinkVariant().equivalentObject =
equivalentObject;
in.variant.hardLinkVariant().equivalentObjectId =
equivalentObject.objectId;
devextras.list_add(in.hardLinks, equivalentObject.hardLinks);
break;
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
case Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN:
/* do nothing */
break;
}
if (yaffs_UpdateObjectHeader(in, name, nameIndex, false, false, 0) < 0) {
/* Could not create the object header, fail the creation */
yaffs_DestroyObject(in);
in = null;
}
}
return in;
}
static yaffs_Object yaffs_MknodFile(yaffs_Object parent, byte[] name, int nameIndex,
/*__u32*/ int mode, /*__u32*/ int uid, /*__u32*/ int gid)
{
return yaffs_MknodObject(Guts_H.YAFFS_OBJECT_TYPE_FILE, parent, name, nameIndex, mode,
uid, gid, null, null, 0, 0);
}
static yaffs_Object yaffs_MknodDirectory(yaffs_Object parent, byte[] name, int nameIndex,
/*__u32*/ int mode, /*__u32*/ int uid, /*__u32*/ int gid)
{
return yaffs_MknodObject(Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, nameIndex,
mode, uid, gid, null, null, 0, 0);
}
static yaffs_Object yaffs_MknodSpecial(yaffs_Object parent, byte[] name, int nameIndex,
/*__u32*/ int mode, /*__u32*/ int uid, /*__u32*/ int gid, /*__u32*/ int rdev)
{
return yaffs_MknodObject(Guts_H.YAFFS_OBJECT_TYPE_SPECIAL, parent, name, nameIndex,
mode, uid, gid, null, null, 0, rdev);
}
static yaffs_Object yaffs_MknodSymLink(yaffs_Object parent, byte[] name, int nameIndex,
/*__u32*/ int mode, /*__u32*/ int uid, /*__u32*/ int gid,
byte[] alias, int aliasIndex)
{
return yaffs_MknodObject(Guts_H.YAFFS_OBJECT_TYPE_SYMLINK, parent, name, nameIndex,
mode, uid, gid, null, alias, aliasIndex, 0);
}
/* yaffs_Link returns the object id of the equivalent object.*/
static yaffs_Object yaffs_Link(yaffs_Object parent, byte[] name, int nameIndex,
yaffs_Object equivalentObject)
{
/* Get the real object in case we were fed a hard link as an equivalent object */
equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
if (yaffs_MknodObject
(Guts_H.YAFFS_OBJECT_TYPE_HARDLINK, parent, name, nameIndex, 0, 0, 0,
equivalentObject, null, 0, 0) != null) {
return equivalentObject;
} else {
return null;
}
}
static boolean yaffs_ChangeObjectName(yaffs_Object obj, yaffs_Object newDir,
byte[] newName, int newNameIndex, boolean force, int shadows)
{
boolean unlinkOp;
boolean deleteOp;
yaffs_Object existingTarget;
if (newDir == null) {
newDir = obj.parent; /* use the old directory */
}
if (newDir.variantType != Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("tragendy: yaffs_ChangeObjectName: newDir is not a directory"
+ ydirectenv.TENDSTR));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
/* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */
if (obj.myDev.subField1.isYaffs2) {
unlinkOp = (newDir == obj.myDev.unlinkedDir);
} else {
unlinkOp = (newDir == obj.myDev.unlinkedDir
&& obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE);
}
deleteOp = (newDir == obj.myDev.deletedDir);
existingTarget = yaffs_FindObjectByName(newDir, newName, newNameIndex);
/* If the object is a file going into the unlinked directory,
* then it is OK to just stuff it in since duplicate names are allowed.
* else only proceed if the new name does not exist and if we're putting
* it into a directory.
*/
if ((unlinkOp ||
deleteOp ||
force ||
(shadows > 0) ||
!(existingTarget != null)) &&
newDir.variantType == Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY) {
yaffs_SetObjectName(obj, newName, newNameIndex);
obj.dirty = true;
yaffs_AddObjectToDirectory(newDir, obj);
if (unlinkOp)
obj.sub.unlinked = true;
/* If it is a deletion then we mark it as a shrink for gc purposes. */
if (yaffs_UpdateObjectHeader(obj, newName, newNameIndex, false, deleteOp, shadows)>= 0)
return Guts_H.YAFFS_OK;
}
return Guts_H.YAFFS_FAIL;
}
static boolean yaffs_RenameObject(yaffs_Object oldDir, byte[] oldName, int oldNameIndex,
yaffs_Object newDir, byte[] newName, int newNameIndex)
{
yaffs_Object obj;
yaffs_Object existingTarget;
boolean force = false;
// #ifdef CONFIG_YAFFS_CASE_INSENSITIVE
// /* Special case for case insemsitive systems (eg. WinCE).
// * While look-up is case insensitive, the name isn't.
// * Therefore we might want to change x.txt to X.txt
// */
// if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0) {
// force = 1;
// }
// #endif
obj = yaffs_FindObjectByName(oldDir, oldName, oldNameIndex);
/* Check new name to long. */
if (obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_SYMLINK &&
ydirectenv.yaffs_strlen(newName, newNameIndex) > Guts_H.YAFFS_MAX_ALIAS_LENGTH)
/* ENAMETOOLONG */
return Guts_H.YAFFS_FAIL;
else if (obj.variantType != Guts_H.YAFFS_OBJECT_TYPE_SYMLINK &&
ydirectenv.yaffs_strlen(newName, newNameIndex) > Guts_H.YAFFS_MAX_NAME_LENGTH)
/* ENAMETOOLONG */
return Guts_H.YAFFS_FAIL;
if (obj != null && obj.renameAllowed) {
/* Now do the handling for an existing target, if there is one */
existingTarget = yaffs_FindObjectByName(newDir, newName, newNameIndex);
if (existingTarget != null &&
existingTarget.variantType == Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY &&
!devextras.list_empty(existingTarget.variant.directoryVariant().children)) {
/* There is a target that is a non-empty directory, so we fail */
return Guts_H.YAFFS_FAIL; /* EEXIST or ENOTEMPTY */
} else if (existingTarget != null && existingTarget != obj) {
/* Nuke the target first, using shadowing,
* but only if it isn't the same object
*/
yaffs_ChangeObjectName(obj, newDir, newName, newNameIndex, force,
existingTarget.objectId);
yaffs_UnlinkObject(existingTarget);
}
return yaffs_ChangeObjectName(obj, newDir, newName, newNameIndex, true, 0);
}
return Guts_H.YAFFS_FAIL;
}
/*------------------------- Block Management and Page Allocation ----------------*/
static boolean yaffs_InitialiseBlocks(yaffs_Device dev) // XXX only done once?
{
int nBlocks = dev.subField2.internalEndBlock - dev.subField2.internalStartBlock + 1;
dev.subField3.allocationBlock = -1; /* force it to get a new one */
/* Todo we're assuming the malloc will pass. */
dev.subField2.blockInfo = /*ydirectenv.YMALLOC(nBlocks * sizeof(yaffs_BlockInfo))*/ ydirectenv.YMALLOC_BLOCKINFO(nBlocks);
if(!(dev.subField2.blockInfo != null)){
// XXX makes no sense in Java
throw new NotImplementedException();
// dev.blockInfo = ydirectenv.YMALLOC_ALyportenv.T(nBlocks * sizeof(yaffs_BlockInfo));
// dev.blockInfoAlt = 1;
}
else
dev.subField2.blockInfoAlt = false;
/* Set up dynamic blockinfo stuff. */
dev.subField3.chunkBitmapStride = (dev.subField1.nChunksPerBlock + 7) / 8; /* round up bytes */
dev.subField2.chunkBits = ydirectenv.YMALLOC(dev.subField3.chunkBitmapStride * nBlocks);
dev.subField2.chunkBitsIndex = 0;
if(!(dev.subField2.chunkBits != null)){
throw new NotImplementedException();
// dev.chunkBits = ydirectenv.YMALLOC_ALyportenv.T(dev.chunkBitmapStride * nBlocks);
// dev.chunkBitsAlt = 1;
}
else
dev.subField2.chunkBitsAlt = false;
if ((dev.subField2.blockInfo != null) && (dev.subField2.chunkBits != null)) {
// PORT already done on object creation
// XXX not if not using pool
Unix.memset(dev.subField2.blockInfo, (byte)0/*, nBlocks * sizeof(yaffs_BlockInfo)*/);
Unix.memset(dev.subField2.chunkBits, dev.subField2.chunkBitsIndex, (byte)0, dev.subField3.chunkBitmapStride * nBlocks);
return Guts_H.YAFFS_OK;
}
return Guts_H.YAFFS_FAIL;
}
static void yaffs_DeinitialiseBlocks(yaffs_Device dev) // XXX
{
if(dev.subField2.blockInfoAlt)
ydirectenv.YFREE_ALT(dev.subField2.blockInfo);
else
ydirectenv.YFREE(dev.subField2.blockInfo);
dev.subField2.blockInfoAlt = false;
dev.subField2.blockInfo = null;
if(dev.subField2.chunkBitsAlt)
ydirectenv.YFREE_ALT(dev.subField2.chunkBits);
else
ydirectenv.YFREE(dev.subField2.chunkBits);
dev.subField2.chunkBitsAlt = false;
dev.subField2.chunkBits = null;
}
static boolean yaffs_BlockNotDisqualifiedFromGC(yaffs_Device dev,
yaffs_BlockInfo bi)
{
int i;
/*__u32*/ long seq;
yaffs_BlockInfo b;
if (!dev.subField1.isYaffs2)
return true; /* disqualification only applies to yaffs2. */
if (!bi.hasShrinkHeader())
return true; /* can gc */
/* Find the oldest dirty sequence number if we don't know it and save it
* so we don't have to keep recomputing it.
*/
if (!(dev.oldestDirtySequence != 0)) {
seq = dev.sequenceNumber;
for (i = dev.subField2.internalStartBlock; i <= dev.subField2.internalEndBlock;
i++) {
b = Guts_H.yaffs_GetBlockInfo(dev, i);
if (b.blockState() == Guts_H.YAFFS_BLOCK_STATE_FULL &&
(b.pagesInUse() - b.softDeletions()) <
dev.subField1.nChunksPerBlock && Utils.intAsUnsignedInt(b.sequenceNumber()) < seq) {
seq = Utils.intAsUnsignedInt(b.sequenceNumber());
}
}
dev.oldestDirtySequence = seq;
}
/* Can't do gc of this block if there are any blocks older than this one that have
* discarded pages.
*/
return (Utils.intAsUnsignedInt(bi.sequenceNumber()) <= dev.oldestDirtySequence);
}
/* FindDiretiestBlock is used to select the dirtiest block (or close enough)
* for garbage collection.
*/
static int _STATIC_LOCAL_yaffs_FindBlockForGarbageCollection_nonAggressiveSkip = 0;
static int yaffs_FindBlockForGarbageCollection(yaffs_Device dev,
boolean aggressive)
{
int b = dev.subField3.currentDirtyChecker;
int i;
int iterations;
int dirtiest = -1;
int pagesInUse = 0; // PORT Compiler is complaining that variable may not have been initialized.
boolean prioritised=false;
yaffs_BlockInfo bi;
//static int nonAggressiveSkip = 0;
boolean pendingPrioritisedExist = false;
/* First let's see if we need to grab a prioritised block */
if(dev.hasPendingPrioritisedGCs){
for(i = dev.subField2.internalStartBlock; i < dev.subField2.internalEndBlock && !prioritised; i++){
bi = Guts_H.yaffs_GetBlockInfo(dev, i);
if(bi.gcPrioritise()) {
pendingPrioritisedExist = true;
if(bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_FULL &&
yaffs_BlockNotDisqualifiedFromGC(dev, bi)){
pagesInUse = (bi.pagesInUse() - bi.softDeletions());
dirtiest = i;
prioritised = true;
aggressive = true; /* Fool the non-aggressive skip logiv below */
}
}
}
if(!pendingPrioritisedExist) /* None found, so we can clear this */
dev.hasPendingPrioritisedGCs = false;
}
/* If we're doing aggressive GC then we are happy to take a less-dirty block, and
* search harder.
* else (we're doing a leasurely gc), then we only bother to do this if the
* block has only a few pages in use.
*/
_STATIC_LOCAL_yaffs_FindBlockForGarbageCollection_nonAggressiveSkip--;
if (!aggressive && (_STATIC_LOCAL_yaffs_FindBlockForGarbageCollection_nonAggressiveSkip > 0)) {
return -1;
}
if(!prioritised)
pagesInUse =
(aggressive) ? dev.subField1.nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
if (aggressive) {
iterations =
dev.subField2.internalEndBlock - dev.subField2.internalStartBlock + 1;
} else {
iterations =
dev.subField2.internalEndBlock - dev.subField2.internalStartBlock + 1;
iterations = iterations / 16;
if (iterations > 200) {
iterations = 200;
}
}
for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) {
b++;
if (b < dev.subField2.internalStartBlock || b > dev.subField2.internalEndBlock) {
b = dev.subField2.internalStartBlock;
}
if (b < dev.subField2.internalStartBlock || b > dev.subField2.internalEndBlock) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("**>> Block %d is not valid" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(b));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
bi = Guts_H.yaffs_GetBlockInfo(dev, b);
// #if 0
// if (bi.blockState == YAFFS_BLOCK_STATE_CHECKPOINT) {
// dirtiest = b;
// pagesInUse = 0;
// }
// else
// #endif
if (bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_FULL &&
(bi.pagesInUse() - bi.softDeletions()) < pagesInUse &&
(yaffs_BlockNotDisqualifiedFromGC(dev, bi))) {
dirtiest = b;
pagesInUse = (bi.pagesInUse() - bi.softDeletions());
}
}
dev.subField3.currentDirtyChecker = b;
if (dirtiest > 0) {
yportenv.T(yportenv.YAFFS_TRACE_GC,
("GC Selected block %d with %d free, prioritised:%b" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dirtiest),
PrimitiveWrapperFactory.get(dev.subField1.nChunksPerBlock - pagesInUse),PrimitiveWrapperFactory.get(prioritised));
}
dev.oldestDirtySequence = 0;
if (dirtiest > 0) {
_STATIC_LOCAL_yaffs_FindBlockForGarbageCollection_nonAggressiveSkip = 4;
}
return dirtiest;
}
static void yaffs_BlockBecameDirty(yaffs_Device dev, int blockNo)
{
yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev, blockNo);
boolean erasedOk = false;
/* If the block is still healthy erase it and mark as clean.
* If the block has had a data failure, then retire it.
*/
yportenv.T(yportenv.YAFFS_TRACE_GC | yportenv.YAFFS_TRACE_ERASE,
("yaffs_BlockBecameDirty block %d state %d %s"+ ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(blockNo), PrimitiveWrapperFactory.get(bi.blockState()), PrimitiveWrapperFactory.get((bi.needsRetiring()) ? "needs retiring" : ""));
bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_DIRTY);
if (!bi.needsRetiring()) {
yaffs_InvalidateCheckpoint(dev);
erasedOk = yaffs_nand_C.yaffs_EraseBlockInNAND(dev, blockNo);
if (!erasedOk) {
dev.subField3.nErasureFailures++;
yportenv.T(yportenv.YAFFS_TRACE_ERROR | yportenv.YAFFS_TRACE_BAD_BLOCKS,
("**>> Erasure failed %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blockNo));
}
}
if (erasedOk && ((yaffs2.utils.Globals.yaffs_traceMask & yportenv.YAFFS_TRACE_ERASE) != 0)) {
int i;
for (i = 0; i < dev.subField1.nChunksPerBlock; i++) {
if (!yaffs_CheckChunkErased
(dev, blockNo * dev.subField1.nChunksPerBlock + i)) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
(">>Block %d erasure supposedly OK, but chunk %d not erased"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blockNo), PrimitiveWrapperFactory.get(i));
}
}
}
if (erasedOk) {
/* Clean it up... */
bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_EMPTY);
dev.subField3.nErasedBlocks++;
bi.setPagesInUse(0);
bi.setSoftDeletions(0);
bi.setHasShrinkHeader(false);
bi.setSkipErasedCheck(true); /* This is clean, so no need to check */
bi.setGcPrioritise(false);
yaffs_ClearChunkBits(dev, blockNo);
yportenv.T(yportenv.YAFFS_TRACE_ERASE,
("Erased block %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blockNo));
} else {
dev.subField3.nFreeChunks -= dev.subField1.nChunksPerBlock; /* We lost a block of free space */
yaffs_RetireBlock(dev, blockNo);
yportenv.T(yportenv.YAFFS_TRACE_ERROR | yportenv.YAFFS_TRACE_BAD_BLOCKS,
("**>> Block %d retired" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blockNo));
}
}
static int yaffs_FindBlockForAllocation(yaffs_Device dev)
{
int i;
yaffs_BlockInfo bi;
if (dev.subField3.nErasedBlocks < 1) {
/* Hoosterman we've got a problem.
* Can't get space to gc
*/
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
(("yaffs tragedy: no more eraased blocks" + ydirectenv.TENDSTR)));
return -1;
}
/* Find an empty block. */
for (i = dev.subField2.internalStartBlock; i <= dev.subField2.internalEndBlock; i++) {
dev.subField3.allocationBlockFinder++;
if (dev.subField3.allocationBlockFinder < dev.subField2.internalStartBlock
|| dev.subField3.allocationBlockFinder > dev.subField2.internalEndBlock) {
dev.subField3.allocationBlockFinder = dev.subField2.internalStartBlock;
}
bi = Guts_H.yaffs_GetBlockInfo(dev, dev.subField3.allocationBlockFinder);
if (bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_EMPTY) {
bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_ALLOCATING);
dev.sequenceNumber++;
bi.setSequenceNumber((int)dev.sequenceNumber);
dev.subField3.nErasedBlocks--;
yportenv.T(yportenv.YAFFS_TRACE_ALLOCATE,
("Allocated block %d, seq %d, %d left" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(dev.subField3.allocationBlockFinder), PrimitiveWrapperFactory.get((int)dev.sequenceNumber),
PrimitiveWrapperFactory.get(dev.subField3.nErasedBlocks));
return dev.subField3.allocationBlockFinder;
}
}
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("yaffs tragedy: no more eraased blocks, but there should have been %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField3.nErasedBlocks));
return -1;
}
// Check if there's space to allocate...
// Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()?
static boolean yaffs_CheckSpaceForAllocation(yaffs_Device dev)
{
int reservedChunks;
int reservedBlocks = dev.subField1.nReservedBlocks;
int checkpointBlocks;
checkpointBlocks = dev.subField1.nCheckpointReservedBlocks - dev.subField2.blocksInCheckpoint;
if(checkpointBlocks < 0)
checkpointBlocks = 0;
reservedChunks = ((reservedBlocks + checkpointBlocks) * dev.subField1.nChunksPerBlock);
return (dev.subField3.nFreeChunks > reservedChunks);
}
/**
*
* @param dev
* @param useReserve
* @param blockUsedPtr Out
* @return
*/
static int yaffs_AllocateChunk(yaffs_Device dev, boolean useReserve, yaffs_BlockInfoPointer blockUsedPtr)
{
int retVal;
yaffs_BlockInfo bi;
if (dev.subField3.allocationBlock < 0) {
/* Get next block to allocate off */
dev.subField3.allocationBlock = yaffs_FindBlockForAllocation(dev);
dev.subField3.allocationPage = 0;
}
if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) {
/* Not enough space to allocate unless we're allowed to use the reserve. */
return -1;
}
if (dev.subField3.nErasedBlocks < dev.subField1.nReservedBlocks
&& dev.subField3.allocationPage == 0) {
yportenv.T(yportenv.YAFFS_TRACE_ALLOCATE, (("Allocating reserve" + ydirectenv.TENDSTR)));
}
/* Next page please.... */
if (dev.subField3.allocationBlock >= 0) {
bi = Guts_H.yaffs_GetBlockInfo(dev, dev.subField3.allocationBlock);
retVal = (dev.subField3.allocationBlock * dev.subField1.nChunksPerBlock) +
dev.subField3.allocationPage;
bi.setPagesInUse(bi.pagesInUse()+ 1);
yaffs_SetChunkBit(dev, dev.subField3.allocationBlock,
dev.subField3.allocationPage);
dev.subField3.allocationPage++;
dev.subField3.nFreeChunks--;
/* If the block is full set the state to full */
if (dev.subField3.allocationPage >= dev.subField1.nChunksPerBlock) {
bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_FULL);
dev.subField3.allocationBlock = -1;
}
if(blockUsedPtr != null)
blockUsedPtr.dereferenced = bi;
return retVal;
}
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" + ydirectenv.TENDSTR));
return -1;
}
static int yaffs_GetErasedChunks(yaffs_Device dev)
{
int n;
n = dev.subField3.nErasedBlocks * dev.subField1.nChunksPerBlock;
if (dev.subField3.allocationBlock > 0) {
n += (dev.subField1.nChunksPerBlock - dev.subField3.allocationPage);
}
return n;
}
static boolean yaffs_GarbageCollectBlock(yaffs_Device dev, int block)
{
int oldChunk;
int newChunk;
int chunkInBlock;
boolean markNAND;
boolean retVal = Guts_H.YAFFS_OK;
int cleanups = 0;
int i;
boolean isCheckpointBlock;
int chunksBefore = yaffs_GetErasedChunks(dev);
int chunksAfter;
yaffs_ExtendedTags tags = new yaffs_ExtendedTags();
yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev, block);
yaffs_Object object;
isCheckpointBlock = (bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_CHECKPOINT);
bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_COLLECTING);
yportenv.T(yportenv.YAFFS_TRACE_TRACING,
("Collecting block %d, in use %d, shrink %b, " + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(block),
PrimitiveWrapperFactory.get(bi.pagesInUse()), PrimitiveWrapperFactory.get(bi.hasShrinkHeader()));
/*yaffs_VerifyFreeChunks(dev); */
bi.setHasShrinkHeader(false); /* 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.subField3.nFreeChunks -= bi.softDeletions();
dev.subField3.isDoingGC = true;
if (isCheckpointBlock ||
!yaffs_StillSomeChunkBits(dev, block)) {
yportenv.T(yportenv.YAFFS_TRACE_TRACING,
("Collecting block %d that has no chunks in use" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(block));
yaffs_BlockBecameDirty(dev, block);
} else {
byte[] buffer = yaffs_GetTempBuffer(dev, 6 /*Utils.__LINE__()*/);
for (chunkInBlock = 0, oldChunk = block * dev.subField1.nChunksPerBlock;
chunkInBlock < dev.subField1.nChunksPerBlock
&& yaffs_StillSomeChunkBits(dev, block);
chunkInBlock++, oldChunk++) {
if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) {
/* This page is in use and might need to be copied off */
markNAND = true;
yaffs_tagsvalidity_C.yaffs_InitialiseTags(tags);
yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk,
buffer, 0, tags);
object =
yaffs_FindObjectByNumber(dev,
tags.objectId);
yportenv.T(yportenv.YAFFS_TRACE_GC_DETAIL,
("Collecting page %d, %d %d %d " + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(chunkInBlock), PrimitiveWrapperFactory.get(tags.objectId), PrimitiveWrapperFactory.get(tags.chunkId),
PrimitiveWrapperFactory.get(tags.byteCount));
if (!(object != null)) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("page %d in gc has no object "
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(oldChunk));
}
if (object != null && object.sub.deleted
&& tags.chunkId != 0) {
/* Data chunk in a deleted file, throw it away
* It's a soft deleted data chunk,
* No need to copy this, just forget about it and
* fix up the object.
*/
object.nDataChunks--;
if (object.nDataChunks <= 0) {
/* remeber to clean up the object */
dev.subField3.gcCleanupList[cleanups] =
tags.objectId;
cleanups++;
}
markNAND = false;
} else if (false
/* Todo object && object.deleted && object.nDataChunks == 0 */
) {
/* Deleted object header with no data chunks.
* Can be discarded and the file deleted.
*/
object.chunkId = 0;
yaffs_FreeTnode(object.myDev,
object.variant.
fileVariant().top);
object.variant.fileVariant().top = null;
yaffs_DoGenericObjectDeletion(object);
} else if (object != null) {
/* It's either a data chunk in a live file or
* an ObjectHeader, so we're interested in it.
* NB Need to keep the ObjectHeaders of deleted files
* until the whole file has been deleted off
*/
tags.serialNumber++;
dev.subField3.nGCCopies++;
if (tags.chunkId == 0) {
/* It is an object Id,
* We need to nuke the shrinkheader flags first
* We no longer want the shrinkHeader flag since its work is done
* and if it is left in place it will mess up scanning.
* Also, clear out any shadowing stuff
*/
yaffs_ObjectHeader oh;
oh = /*(yaffs_ObjectHeader)buffer*/ new yaffs_ObjectHeader(buffer, 0);
oh.setIsShrink(false);
oh.setShadowsObject(-1);
tags.extraShadows = false;
tags.extraIsShrinkHeader = false;
}
newChunk =
yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, 0, tags, true);
if (newChunk < 0) {
retVal = Guts_H.YAFFS_FAIL;
} else {
/* Ok, now fix up the Tnodes etc. */
if (tags.chunkId == 0) {
/* It's a header */
object.chunkId = newChunk;
object.serial = (byte)tags.serialNumber;
} else {
/* It's a data chunk */
yaffs_PutChunkIntoFile
(object,
tags.chunkId,
newChunk, 0);
}
}
}
yaffs_DeleteChunk(dev, oldChunk, markNAND, 7 /*Utils.__LINE__()*/);
}
}
yaffs_ReleaseTempBuffer(dev, buffer, 8 /*Utils.__LINE__()*/);
/* Do any required cleanups */
for (i = 0; i < cleanups; i++) {
/* Time to delete the file too */
object =
yaffs_FindObjectByNumber(dev,
dev.subField3.gcCleanupList[i]);
if (object != null) {
yaffs_FreeTnode(dev,
object.variant.fileVariant().
top);
object.variant.fileVariant().top = null;
yportenv.T(yportenv.YAFFS_TRACE_GC,
("yaffs: About to finally delete object %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(object.objectId));
yaffs_DoGenericObjectDeletion(object);
object.myDev.nDeletedFiles--;
}
}
}
if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) {
yportenv.T(yportenv.YAFFS_TRACE_GC,
("gc did not increase free chunks before %d after %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(chunksBefore), PrimitiveWrapperFactory.get(chunksAfter));
}
dev.subField3.isDoingGC = false;
return Guts_H.YAFFS_OK;
}
/* New garbage collector
* If we're very low on erased blocks then we do aggressive garbage collection
* otherwise we do "leasurely" garbage collection.
* Aggressive gc looks further (whole array) and will accept less dirty blocks.
* Passive gc only inspects smaller areas and will only accept more dirty blocks.
*
* The idea is to help clear out space in a more spread-out manner.
* Dunno if it really does anything useful.
*/
static boolean yaffs_CheckGarbageCollection(yaffs_Device dev)
{
int block;
boolean aggressive;
boolean gcOk = Guts_H.YAFFS_OK;
int maxTries = 0;
int checkpointBlockAdjust;
if (dev.subField3.isDoingGC) {
/* Bail out so we don't get recursive gc */
return Guts_H.YAFFS_OK;
}
/* This loop should pass the first time.
* We'll only see looping here if the erase of the collected block fails.
*/
do {
maxTries++;
checkpointBlockAdjust = (dev.subField1.nCheckpointReservedBlocks - dev.subField2.blocksInCheckpoint);
if(checkpointBlockAdjust < 0)
checkpointBlockAdjust = 0;
if (dev.subField3.nErasedBlocks < (dev.subField1.nReservedBlocks + checkpointBlockAdjust)) {
/* We need a block soon...*/
aggressive = true;
} else {
/* We're in no hurry */
aggressive = false;
}
block = yaffs_FindBlockForGarbageCollection(dev, aggressive);
if (block > 0) {
dev.subField3.garbageCollections++;
if (!aggressive) {
dev.subField3.passiveGarbageCollections++;
}
yportenv.T(yportenv.YAFFS_TRACE_GC,
("yaffs: GC erasedBlocks %d aggressive %b" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(dev.subField3.nErasedBlocks), PrimitiveWrapperFactory.get(aggressive));
gcOk = yaffs_GarbageCollectBlock(dev, block);
}
if (dev.subField3.nErasedBlocks < (dev.subField1.nReservedBlocks) && block > 0) {
yportenv.T(yportenv.YAFFS_TRACE_GC,
("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField3.nErasedBlocks), PrimitiveWrapperFactory.get(maxTries), PrimitiveWrapperFactory.get(block));
}
} while ((dev.subField3.nErasedBlocks < dev.subField1.nReservedBlocks) && (block > 0)
&& (maxTries < 2));
return aggressive ? gcOk : Guts_H.YAFFS_OK;
}
/*------------------------- TAGS --------------------------------*/
static boolean yaffs_TagsMatch(yaffs_ExtendedTags tags, int objectId,
int chunkInObject)
{
return (tags.chunkId == chunkInObject &&
tags.objectId == objectId && !tags.chunkDeleted) ? true : false;
}
/*-------------------- Data file manipulation -----------------*/
static int yaffs_FindChunkInFile(yaffs_Object in, int chunkInInode,
yaffs_ExtendedTags tags)
{
/*Get the Tnode, then get the level 0 offset chunk offset */
yaffs_Tnode tn;
int theChunk = -1;
yaffs_ExtendedTags localTags = new yaffs_ExtendedTags();
int retVal = -1;
yaffs_Device dev = in.myDev;
if (!(tags != null)) {
/* Passed a null, so use our own tags space */
tags = localTags;
}
tn = yaffs_FindLevel0Tnode(dev, in.variant.fileVariant(), chunkInInode);
if (tn != null) {
theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
retVal =
yaffs_FindChunkInGroup(dev, theChunk, tags, in.objectId,
chunkInInode);
}
return retVal;
}
static int yaffs_FindAndDeleteChunkInFile(yaffs_Object in, int chunkInInode,
yaffs_ExtendedTags tags)
{
/* Get the Tnode, then get the level 0 offset chunk offset */
yaffs_Tnode tn;
int theChunk = -1;
yaffs_ExtendedTags localTags = new yaffs_ExtendedTags();
yaffs_Device dev = in.myDev;
int retVal = -1;
if (!(tags != null)) {
/* Passed a null, so use our own tags space */
tags = localTags;
}
tn = yaffs_FindLevel0Tnode(dev, in.variant.fileVariant(), chunkInInode);
if ((tn != null)) {
theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
retVal =
yaffs_FindChunkInGroup(dev, theChunk, tags, in.objectId,
chunkInInode);
/* Delete the entry in the filestructure (if found) */
if (retVal != -1) {
yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0);
}
} else {
/*yportenv.T(("No level 0 found for %d\n", chunkInInode)); */
}
if (retVal == -1) {
/* yportenv.T(("Could not find %d to delete\n",chunkInInode)); */
}
return retVal;
}
// dev is not defined
// #ifdef YAFFS_PARANOID
// static boolean yaffs_CheckFileSanity(yaffs_Object in)
// {
// int chunk;
// int nChunks;
// int fSize;
// boolean failed = false;
// int objId;
// yaffs_Tnode tn;
// yaffs_Tags localTags = new yaffs_Tags();
// yaffs_Tags tags = localTags;
// int theChunk;
// boolean chunkDeleted;
// if (in.variantType != Guts_H.YAFFS_OBJECT_TYPE_FILE) {
// /* yportenv.T(("Object not a file\n")); */
// return Guts_H.YAFFS_FAIL;
// }
// objId = in.objectId;
// fSize = in.variant.fileVariant().fileSize;
// nChunks =
// (fSize + in.myDev.nDataBytesPerChunk - 1) / in.myDev.nDataBytesPerChunk;
// for (chunk = 1; chunk <= nChunks; chunk++) {
// tn = yaffs_FindLevel0Tnode(in.myDev, in.variant.fileVariant(),
// chunk);
// if (tn != null) {
// theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk);
// if (yaffs_CheckChunkBits
// (dev, theChunk / dev.nChunksPerBlock,
// theChunk % dev.nChunksPerBlock)) {
// yaffs_ReadChunkTagsFromNAND(in.myDev, theChunk,
// tags,
// chunkDeleted);
// if (yaffs_TagsMatch
// (tags, in.objectId, chunk, chunkDeleted)) {
// /* found it; */
// }
// } else {
// failed = true;
// }
// } else {
// /* yportenv.T(("No level 0 found for %d\n", chunk)); */
// }
// }
// return failed ? Guts_H.YAFFS_FAIL : Guts_H.YAFFS_OK;
// }
// #endif
static boolean yaffs_PutChunkIntoFile(yaffs_Object in, int chunkInInode,
int chunkInNAND, int inScan)
{
/* NB inScan is zero unless scanning.
* For forward scanning, inScan is > 0;
* for backward scanning inScan is < 0
*/
yaffs_Tnode tn;
yaffs_Device dev = in.myDev;
int existingChunk;
yaffs_ExtendedTags existingTags = new yaffs_ExtendedTags();
yaffs_ExtendedTags newTags = new yaffs_ExtendedTags();
/*unsigned*/ int existingSerial, newSerial;
if (in.variantType != Guts_H.YAFFS_OBJECT_TYPE_FILE) {
/* Just ignore an attempt at putting a chunk into a non-file during scanning
* If it is not during Scanning then something went wrong!
*/
if (!(inScan != 0)) {
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("yaffs tragedy:attempt to put data chunk into a non-file"
+ ydirectenv.TENDSTR));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
yaffs_DeleteChunk(dev, chunkInNAND, true, 9 /*Utils.__LINE__()*/);
return Guts_H.YAFFS_OK;
}
tn = yaffs_AddOrFindLevel0Tnode(dev,
in.variant.fileVariant(),
chunkInInode,
null);
if (!(tn != null)) {
return Guts_H.YAFFS_FAIL;
}
existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode);
if (inScan != 0) {
/* If we're scanning then we need to test for duplicates
* NB This does not need to be efficient since it should only ever
* happen when the power fails during a write, then only one
* chunk should ever be affected.
*
* Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
* Update: For backward scanning we don't need to re-read tags so this is quite cheap.
*/
if (existingChunk != 0) {
/* NB Right now existing chunk will not be real chunkId if the device >= 32MB
* thus we have to do a FindChunkInFile to get the real chunk id.
*
* We have a duplicate now we need to decide which one to use:
*
* Backwards scanning YAFFS2: The old one is what we use, dump the new one.
* Forward scanning YAFFS2: The new one is what we use, dump the old one.
* YAFFS1: Get both sets of tags and compare serial numbers.
*/
if (inScan > 0) {
/* Only do this for forward scanning */
yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev,
chunkInNAND,
null, 0, newTags);
/* Do a proper find */
existingChunk =
yaffs_FindChunkInFile(in, chunkInInode,
existingTags);
}
if (existingChunk <= 0) {
/*Hoosterman - how did this happen? */
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("yaffs tragedy: existing chunk < 0 in scan"
+ ydirectenv.TENDSTR));
}
/* NB The deleted flags should be false, otherwise the chunks will
* not be loaded during a scan
*/
newSerial = newTags.serialNumber;
existingSerial = existingTags.serialNumber;
if ((inScan > 0) &&
(in.myDev.subField1.isYaffs2 ||
existingChunk <= 0 ||
((existingSerial + 1) & 3) == newSerial)) {
/* Forward scanning.
* Use new
* Delete the old one and drop through to update the tnode
*/
yaffs_DeleteChunk(dev, existingChunk, true,
10 /*Utils.__LINE__()*/);
} else {
/* Backward scanning or we want to use the existing one
* Use existing.
* Delete the new one and return early so that the tnode isn't changed
*/
yaffs_DeleteChunk(dev, chunkInNAND, true,
11 /*Utils.__LINE__()*/);
return Guts_H.YAFFS_OK;
}
}
}
if (existingChunk == 0) {
in.nDataChunks++;
}
yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND);
return Guts_H.YAFFS_OK;
}
static boolean yaffs_ReadChunkDataFromObject(yaffs_Object in, int chunkInInode,
byte[] buffer, int bufferIndex)
{
int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, null);
if (chunkInNAND >= 0) {
return yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(in.myDev, chunkInNAND,
buffer,bufferIndex,null);
} else {
yportenv.T(yportenv.YAFFS_TRACE_NANDACCESS,
("Chunk %d not found zero instead" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(chunkInNAND));
/* get sane (zero) data if you read a hole */
Unix.memset(buffer, bufferIndex, (byte)0, in.myDev.subField1.nDataBytesPerChunk);
return false;
}
}
static void yaffs_DeleteChunk(yaffs_Device dev, int chunkId, boolean markNAND, int lyn)
{
int block;
int page;
yaffs_ExtendedTags tags = new yaffs_ExtendedTags();
yaffs_BlockInfo bi;
if (chunkId <= 0)
return;
dev.nDeletions++;
block = chunkId / dev.subField1.nChunksPerBlock;
page = chunkId % dev.subField1.nChunksPerBlock;
bi = Guts_H.yaffs_GetBlockInfo(dev, block);
yportenv.T(yportenv.YAFFS_TRACE_DELETION,
("line %d delete of chunk %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(lyn), PrimitiveWrapperFactory.get(chunkId));
if (markNAND &&
bi.blockState() != Guts_H.YAFFS_BLOCK_STATE_COLLECTING && !dev.subField1.isYaffs2) {
yaffs_tagsvalidity_C.yaffs_InitialiseTags(tags);
tags.chunkDeleted = true;
yaffs_nand_C.yaffs_WriteChunkWithTagsToNAND(dev, chunkId, null, 0, tags);
yaffs_HandleUpdateChunk(dev, chunkId, tags);
} else {
dev.nUnmarkedDeletions++;
}
/* Pull out of the management area.
* If the whole block became dirty, this will kick off an erasure.
*/
if (bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_ALLOCATING ||
bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_FULL ||
bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_COLLECTING) {
dev.subField3.nFreeChunks++;
yaffs_ClearChunkBit(dev, block, page);
bi.setPagesInUse(bi.pagesInUse()-1);
if (bi.pagesInUse() == 0 &&
!bi.hasShrinkHeader() &&
bi.blockState() != Guts_H.YAFFS_BLOCK_STATE_ALLOCATING &&
bi.blockState() != Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
yaffs_BlockBecameDirty(dev, block);
}
} else {
/* yportenv.T(("Bad news deleting chunk %d\n",chunkId)); */
}
}
static int yaffs_WriteChunkDataToObject(yaffs_Object in, int chunkInInode,
byte[] buffer, int bufferIndex, int nBytes,
boolean useReserve)
{
/* Find old chunk Need to do this to get serial number
* Write new one and patch into tree.
* Invalidate old tags.
*/
int prevChunkId;
yaffs_ExtendedTags prevTags = new yaffs_ExtendedTags();
int newChunkId;
yaffs_ExtendedTags newTags = new yaffs_ExtendedTags();
yaffs_Device dev = in.myDev;
yaffs_CheckGarbageCollection(dev);
/* Get the previous chunk at this location in the file if it exists */
prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, prevTags);
/* Set up new tags */
yaffs_tagsvalidity_C.yaffs_InitialiseTags(newTags);
newTags.chunkId = chunkInInode;
newTags.objectId = in.objectId;
newTags.serialNumber =
((prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1);
newTags.byteCount = nBytes;
newChunkId =
yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, bufferIndex, newTags,
useReserve);
if (newChunkId >= 0) {
yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0);
if (prevChunkId >= 0) {
yaffs_DeleteChunk(dev, prevChunkId, true, 12 /*Utils.__LINE__()*/);
}
yaffs_CheckFileSanity(in);
}
return newChunkId;
}
/* UpdateObjectHeader updates the header on NAND for an object.
* If name is not null, then that new name is used.
*/
static int yaffs_UpdateObjectHeader(yaffs_Object in, byte[] name, int nameIndex,
boolean force, boolean isShrink, int shadows)
{
yaffs_BlockInfo bi;
yaffs_Device dev = in.myDev;
int prevChunkId;
int retVal = 0;
boolean result = false;
int newChunkId;
yaffs_ExtendedTags newTags = new yaffs_ExtendedTags();
byte[] buffer = null; final int bufferIndex = 0;
byte[] oldName = new byte[Guts_H.YAFFS_MAX_NAME_LENGTH + 1]; final int oldNameIndex = 0;
yaffs_ObjectHeader oh = null;
if (!in.sub.fake || force) {
yaffs_CheckGarbageCollection(dev);
buffer = yaffs_GetTempBuffer(in.myDev, 13 /*Utils.__LINE__()*/);
oh = /*(yaffs_ObjectHeader *) buffer*/ new yaffs_ObjectHeader(buffer,0);
prevChunkId = in.chunkId;
if (prevChunkId >= 0) {
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId,
buffer, bufferIndex, null);
Unix.memcpy(oldName, oldNameIndex, oh.name(), oh.nameIndex(), (yaffs_ObjectHeader.SIZEOF_name));
}
Unix.memset(buffer, bufferIndex, (byte)0xFF, dev.subField1.nDataBytesPerChunk);
oh.setType(in.variantType);
oh.setYst_mode(in.yst_mode);
oh.setShadowsObject(shadows);
// #ifdef CONFIG_YAFFS_WINCE
// oh.win_atime[0] = in.win_atime[0];
// oh.win_ctime[0] = in.win_ctime[0];
// oh.win_mtime[0] = in.win_mtime[0];
// oh.win_atime[1] = in.win_atime[1];
// oh.win_ctime[1] = in.win_ctime[1];
// oh.win_mtime[1] = in.win_mtime[1];
// #else
oh.setYst_uid(in.yst_uid);
oh.setYst_gid(in.yst_gid);
oh.setYst_atime(in.yst_atime);
oh.setYst_mtime(in.yst_mtime);
oh.setYst_ctime(in.yst_ctime);
oh.setYst_rdev(in.yst_rdev);
// #endif
if (in.parent != null) {
oh.setParentObjectId(in.parent.objectId);
} else {
oh.setParentObjectId(0);
}
if (name != null && name[nameIndex] != 0) {
Unix.memset(oh.name(), oh.nameIndex(), (byte)0, yaffs_ObjectHeader.SIZEOF_name);
ydirectenv.yaffs_strncpy(oh.name(), oh.nameIndex(), name, nameIndex, Guts_H.YAFFS_MAX_NAME_LENGTH);
} else if (prevChunkId != 0) {
Unix.memcpy(oh.name(), oh.nameIndex(), oldName, oldNameIndex, yaffs_ObjectHeader.SIZEOF_name);
} else {
Unix.memset(oh.name(), oh.nameIndex(), (byte)0, yaffs_ObjectHeader.SIZEOF_name);
}
oh.setIsShrink(isShrink);
switch (in.variantType) {
case Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN:
/* Should not happen */
break;
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
oh.setFileSize(
(oh.parentObjectId() == Guts_H.YAFFS_OBJECTID_DELETED
|| oh.parentObjectId() ==
Guts_H.YAFFS_OBJECTID_UNLINKED) ? 0 : in.variant.
fileVariant().fileSize);
break;
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
oh.setEquivalentObjectId(
in.variant.hardLinkVariant().equivalentObjectId);
break;
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
/* Do nothing */
break;
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
/* Do nothing */
break;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
ydirectenv.yaffs_strncpy(oh.alias(), oh.aliasIndex(),
in.variant.symLinkVariant().alias,
in.variant.symLinkVariant().aliasIndex,
Guts_H.YAFFS_MAX_ALIAS_LENGTH);
oh.alias()[oh.aliasIndex()+Guts_H.YAFFS_MAX_ALIAS_LENGTH] = 0;
break;
}
/* Tags */
yaffs_tagsvalidity_C.yaffs_InitialiseTags(newTags);
in.serial++;
newTags.chunkId = 0;
newTags.objectId = in.objectId;
newTags.serialNumber = Utils.byteAsUnsignedByte(in.serial);
/* Add extra info for file header */
newTags.extraHeaderInfoAvailable = true;
newTags.extraParentObjectId = oh.parentObjectId();
newTags.extraFileLength = oh.fileSize();
newTags.extraIsShrinkHeader = oh.isShrink();
newTags.extraEquivalentObjectId = oh.equivalentObjectId();
newTags.extraShadows = (oh.shadowsObject() > 0) ? true : false;
newTags.extraObjectType = in.variantType;
/* Create new chunk in NAND */
newChunkId =
yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, bufferIndex, newTags,
(prevChunkId >= 0) ? true : false);
if (newChunkId >= 0) {
in.chunkId = newChunkId;
if (prevChunkId >= 0) {
yaffs_DeleteChunk(dev, prevChunkId, true,
14 /*Utils.__LINE__()*/);
}
if(!yaffs_ObjectHasCachedWriteData(in))
in.dirty = false;
/* If this was a shrink, then mark the block that the chunk lives on */
if (isShrink) {
bi = Guts_H.yaffs_GetBlockInfo(in.myDev,
newChunkId /in.myDev. subField1.nChunksPerBlock);
bi.setHasShrinkHeader(true);
}
}
retVal = newChunkId;
}
if (buffer != null)
yaffs_ReleaseTempBuffer(dev, buffer, 15 /*Utils.__LINE__()*/);
return retVal;
}
/*------------------------ Short Operations Cache ----------------------------------------
* In many situations where there is no high level buffering (eg WinCE) a lot of
* reads might be short sequential reads, and a lot of writes may be short
* sequential writes. eg. scanning/writing a jpeg file.
* In these cases, a short read/write cache can provide a huge perfomance benefit
* with dumb-as-a-rock code.
* In Linux, the page cache provides read buffering aand the short op cache provides write
* buffering.
*
* There are a limited number (~10) of cache chunks per device so that we don't
* need a very intelligent search.
*/
static boolean yaffs_ObjectHasCachedWriteData(yaffs_Object obj)
{
yaffs_Device dev = obj.myDev;
int i;
yaffs_ChunkCache cache;
int nCaches = obj.myDev.subField1.nShortOpCaches;
for(i = 0; i < nCaches; i++){
cache = dev.srCache[i];
if (cache.object == obj &&
cache.dirty)
return true;
}
return false;
}
static void yaffs_FlushFilesChunkCache(yaffs_Object obj)
{
yaffs_Device dev = obj.myDev;
int lowest = -99; /* Stop compiler whining. */
int i;
yaffs_ChunkCache cache;
int chunkWritten = 0;
int nCaches = obj.myDev.subField1.nShortOpCaches;
if (nCaches > 0) {
do {
cache = null;
/* Find the dirty cache for this object with the lowest chunk id. */
for (i = 0; i < nCaches; i++) {
if (dev.srCache[i].object == obj &&
dev.srCache[i].dirty) {
if (!(cache != null)
|| dev.srCache[i].chunkId <
lowest) {
cache = dev.srCache[i]; // nBytes given 156
lowest = cache.chunkId;
}
}
}
if ((cache != null) && !cache.locked) {
/* Write it out and free it up */
chunkWritten =
yaffs_WriteChunkDataToObject(cache.object,
cache.chunkId,
cache.data, cache.dataIndex,
cache.nBytes,
true);
cache.dirty = false;
cache.object = null;
}
} while ((cache != null) && chunkWritten > 0);
if (cache != null) {
/* Hoosterman, disk full while writing cache out. */
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
(("yaffs tragedy: no space during cache write" + ydirectenv.TENDSTR)));
}
}
}
/*yaffs_FlushEntireDeviceCache(dev)
*
*
*/
static void yaffs_FlushEntireDeviceCache(yaffs_Device dev)
{
yaffs_Object obj;
int nCaches = dev.subField1.nShortOpCaches;
int i;
/* Find a dirty object in the cache and flush it...
* until there are no further dirty objects.
*/
do {
obj = null;
for( i = 0; i < nCaches && !(obj != null); i++) {
if ((dev.srCache[i].object != null) &&
dev.srCache[i].dirty)
obj = dev.srCache[i].object;
}
if(obj != null)
yaffs_FlushFilesChunkCache(obj);
} while(obj != null);
}
/* Grab us a cache chunk for use.
* First look for an empty one.
* Then look for the least recently used non-dirty one.
* Then look for the least recently used dirty one...., flush and look again.
*/
static yaffs_ChunkCache yaffs_GrabChunkCacheWorker(yaffs_Device dev)
{
int i;
int usage;
int theOne;
if (dev.subField1.nShortOpCaches > 0) {
for (i = 0; i < dev.subField1.nShortOpCaches; i++) {
if (!(dev.srCache[i].object != null))
return dev.srCache[i];
}
return null;
// PORT the Java compiler whines about the rest of the block to be not reachable
// theOne = -1;
// usage = 0; /* just to stop the compiler grizzling */
// for (i = 0; i < dev.nShortOpCaches; i++) {
// if (!dev.srCache[i].dirty &&
// ((dev.srCache[i].lastUse < usage && theOne >= 0) ||
// theOne < 0)) {
// usage = dev.srCache[i].lastUse;
// theOne = i;
// }
// }
// return theOne >= 0 ? dev.srCache[theOne] : null;
} else {
return null;
}
}
static yaffs_ChunkCache yaffs_GrabChunkCache(yaffs_Device dev)
{
yaffs_ChunkCache cache;
yaffs_Object theObj;
int usage;
int i;
int pushout;
if (dev.subField1.nShortOpCaches > 0) {
/* Try find a non-dirty one... */
cache = yaffs_GrabChunkCacheWorker(dev);
if (!(cache != null)) {
/* They were all dirty, find the last recently used object and flush
* its cache, then find again.
* NB what's here is not very accurate, we actually flush the object
* the last recently used page.
*/
/* With locking we can't assume we can use entry zero */
theObj = null;
usage = -1;
cache = null;
pushout = -1;
for (i = 0; i < dev.subField1.nShortOpCaches; i++) {
if ((dev.srCache[i].object != null) &&
!dev.srCache[i].locked &&
(dev.srCache[i].lastUse < usage || !(cache != null)))
{
usage = dev.srCache[i].lastUse;
theObj = dev.srCache[i].object;
cache = dev.srCache[i];
pushout = i;
}
}
if (!(cache != null) || cache.dirty) {
/* Flush and try again */
yaffs_FlushFilesChunkCache(theObj);
cache = yaffs_GrabChunkCacheWorker(dev);
}
}
return cache;
} else
return null;
}
/* Find a cached chunk */
static yaffs_ChunkCache yaffs_FindChunkCache(yaffs_Object obj,
int chunkId)
{
yaffs_Device dev = obj.myDev;
int i;
if (dev.subField1.nShortOpCaches > 0) {
for (i = 0; i < dev.subField1.nShortOpCaches; i++) {
if (dev.srCache[i].object == obj &&
dev.srCache[i].chunkId == chunkId) {
dev.cacheHits++;
return dev.srCache[i];
}
}
}
return null;
}
/* Mark the chunk for the least recently used algorithym */
static void yaffs_UseChunkCache(yaffs_Device dev, yaffs_ChunkCache cache,
boolean isAWrite)
{
if (dev.subField1.nShortOpCaches > 0) {
if (dev.srLastUse < 0 || dev.srLastUse > 100000000) {
/* Reset the cache usages */
int i;
for (i = 1; i < dev.subField1.nShortOpCaches; i++) {
dev.srCache[i].lastUse = 0;
}
dev.srLastUse = 0;
}
dev.srLastUse++;
cache.lastUse = dev.srLastUse;
if (isAWrite) {
cache.dirty = true;
}
}
}
/* Invalidate a single cache page.
* Do this when a whole page gets written,
* ie the short cache for this page is no longer valid.
*/
static void yaffs_InvalidateChunkCache(yaffs_Object object, int chunkId)
{
if (object.myDev.subField1.nShortOpCaches > 0) {
yaffs_ChunkCache cache = yaffs_FindChunkCache(object, chunkId);
if (cache != null) {
cache.object = null;
}
}
}
/* Invalidate all the cache pages associated with this object
* Do this whenever ther file is deleted or resized.
*/
static void yaffs_InvalidateWholeChunkCache(yaffs_Object in)
{
int i;
yaffs_Device dev = in.myDev;
if (dev.subField1.nShortOpCaches > 0) {
/* Invalidate it. */
for (i = 0; i < dev.subField1.nShortOpCaches; i++) {
if (dev.srCache[i].object == in) {
dev.srCache[i].object = null;
}
}
}
}
/*--------------------- Checkpointing --------------------*/
static boolean yaffs_WriteCheckpointValidityMarker(yaffs_Device dev,int head)
{
yaffs_CheckpointValidity cp = new yaffs_CheckpointValidity();
cp.setStructType(/*sizeof(cp)*/ yaffs_CheckpointValidity.SERIALIZED_LENGTH);
cp.setMagic(Guts_H.YAFFS_MAGIC);
cp.setVersion(Guts_H.YAFFS_CHECKPOINT_VERSION);
cp.setHead((head != 0) ? 1 : 0);
return (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,cp.serialized,cp.offset,
/*sizeof(cp)*/ yaffs_CheckpointValidity.SERIALIZED_LENGTH)
== /*sizeof(cp)*/ yaffs_CheckpointValidity.SERIALIZED_LENGTH) ? true : false;
}
static boolean yaffs_ReadCheckpointValidityMarker(yaffs_Device dev, int head)
{
yaffs_CheckpointValidity cp = new yaffs_CheckpointValidity();
boolean ok;
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,cp.serialized,cp.offset,
yaffs_CheckpointValidity.SERIALIZED_LENGTH) == yaffs_CheckpointValidity.SERIALIZED_LENGTH);
if(ok)
ok = (cp.structType() == yaffs_CheckpointValidity.SERIALIZED_LENGTH) &&
(cp.magic() == Guts_H.YAFFS_MAGIC) &&
(cp.version() == Guts_H.YAFFS_CHECKPOINT_VERSION) &&
(cp.head() == ((head != 0) ? 1 : 0));
return (ok) ? true : false;
}
static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice cp,
yaffs_Device dev)
{
cp.setNErasedBlocks(dev.subField3.nErasedBlocks);
cp.setAllocationBlock(dev.subField3.allocationBlock);
cp.setAllocationPage(dev.subField3.allocationPage);
cp.setNFreeChunks(dev.subField3.nFreeChunks);
cp.setNDeletedFiles(dev.nDeletedFiles);
cp.setNUnlinkedFiles(dev.nUnlinkedFiles);
cp.setNBackgroundDeletions(dev.nBackgroundDeletions);
cp.setSequenceNumber((int)dev.sequenceNumber);
cp.setOldestDirtySequence((int)dev.oldestDirtySequence);
}
static void yaffs_CheckpointDeviceToDevice(yaffs_Device dev,
yaffs_CheckpointDevice cp)
{
dev.subField3.nErasedBlocks = cp.nErasedBlocks();
dev.subField3.allocationBlock = cp.allocationBlock();
dev.subField3.allocationPage = cp.allocationPage();
dev.subField3.nFreeChunks = cp.nFreeChunks();
dev.nDeletedFiles = cp.nDeletedFiles();
dev.nUnlinkedFiles = cp.nUnlinkedFiles();
dev.nBackgroundDeletions = cp.nBackgroundDeletions();
dev.sequenceNumber = Utils.intAsUnsignedInt(cp.sequenceNumber());
dev.oldestDirtySequence = Utils.intAsUnsignedInt(cp.oldestDirtySequence());
}
static boolean yaffs_WriteCheckpointDevice(yaffs_Device dev)
{
yaffs_CheckpointDevice cp = new yaffs_CheckpointDevice();
/**__u32*/ int nBytes;
/**__u32*/ int nBlocks = (dev.subField2.internalEndBlock - dev.subField2.internalStartBlock + 1);
boolean ok;
/* Write device runtime values*/
yaffs_DeviceToCheckpointDevice(cp,dev);
cp.setStructType(yaffs_CheckpointDevice.SERIALIZED_LENGTH);
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,cp.serialized,cp.offset,yaffs_CheckpointDevice.SERIALIZED_LENGTH) == yaffs_CheckpointDevice.SERIALIZED_LENGTH);
/* Write block info */
if(ok) {
nBytes = nBlocks * yaffs_BlockInfo.SERIALIZED_LENGTH;
// PORT We want to write the data for the whole array.
// The data for all yaffs_BlockInfo array members is stored in the same array.
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,dev.subField2.blockInfo[0].serialized,
dev.subField2.blockInfo[0].offset,nBytes) == nBytes);
}
/* Write chunk bits */
if(ok) {
nBytes = nBlocks * dev.subField3.chunkBitmapStride;
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,dev.subField2.chunkBits,dev.subField2.chunkBitsIndex,nBytes) == nBytes);
}
return ok ? true : false;
}
static boolean yaffs_ReadCheckpointDevice(yaffs_Device dev)
{
yaffs_CheckpointDevice cp = new yaffs_CheckpointDevice();
/**__u32*/ int nBytes;
/**__u32*/ int nBlocks = (dev.subField2.internalEndBlock - dev.subField2.internalStartBlock + 1);
boolean ok;
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,cp.serialized,cp.offset,yaffs_CheckpointDevice.SERIALIZED_LENGTH) == yaffs_CheckpointDevice.SERIALIZED_LENGTH);
if(!(ok))
return false;
if(cp.structType() != yaffs_CheckpointDevice.SERIALIZED_LENGTH)
return false;
yaffs_CheckpointDeviceToDevice(dev,cp);
nBytes = nBlocks * yaffs_BlockInfo.SERIALIZED_LENGTH;
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,dev.subField2.blockInfo[0].serialized,dev.subField2.blockInfo[0].offset,
nBytes) == nBytes);
if(!(ok))
return false;
nBytes = nBlocks * dev.subField3.chunkBitmapStride;
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,dev.subField2.chunkBits,dev.subField2.chunkBitsIndex,nBytes) == nBytes);
return ok ? true : false;
}
static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject cp,
yaffs_Object obj)
{
cp.setObjectId(obj.objectId);
cp.setParentId((obj.parent != null) ? obj.parent.objectId : 0);
cp.setChunkId(obj.chunkId);
cp.setVariantType(obj.variantType);
cp.setDeleted(obj.sub.deleted);
cp.setSoftDeleted(obj.sub.softDeleted);
cp.setUnlinked(obj.sub.unlinked);
cp.setFake(obj.sub.fake);
cp.setRenameAllowed(obj.renameAllowed);
cp.setUnlinkAllowed(obj.unlinkAllowed);
cp.setSerial(obj.serial);
cp.setNDataChunks(obj.nDataChunks);
if(obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE)
cp.setFileSizeOrEquivalentObjectId(obj.variant.fileVariant().fileSize);
else if(obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_HARDLINK)
cp.setFileSizeOrEquivalentObjectId(obj.variant.hardLinkVariant().equivalentObjectId);
}
static void yaffs_CheckpointObjectToObject( yaffs_Object obj,yaffs_CheckpointObject cp)
{
yaffs_Object parent;
obj.objectId = cp.objectId();
if(cp.parentId() != 0)
parent = yaffs_FindOrCreateObjectByNumber(
obj.myDev,
cp.parentId(),
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY);
else
parent = null;
if(parent != null)
yaffs_AddObjectToDirectory(parent, obj);
obj.chunkId = cp.chunkId();
obj.variantType = cp.variantType();
obj.sub.deleted = cp.deleted();
obj.sub.softDeleted = cp.softDeleted();
obj.sub.unlinked = cp.unlinked();
obj.sub.fake = cp.fake();
obj.renameAllowed = cp.renameAllowed();
obj.unlinkAllowed = cp.unlinkAllowed();
obj.serial = cp.serial();
obj.nDataChunks = cp.nDataChunks();
if(obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE)
obj.variant.fileVariant().fileSize = cp.fileSizeOrEquivalentObjectId();
else if(obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_HARDLINK)
obj.variant.hardLinkVariant().equivalentObjectId = cp.fileSizeOrEquivalentObjectId();
if(obj.objectId >= Guts_H.YAFFS_NOBJECT_BUCKETS)
obj.lazyLoaded = true;
}
// PORT Only leafs (16 bit page address) are written here.
static boolean yaffs_CheckpointTnodeWorker(yaffs_Object in, yaffs_Tnode tn,
/**__u32*/ int level, int chunkOffset)
{
int i;
yaffs_Device dev = in.myDev;
boolean ok = true;
int nTnodeBytes = (dev.subField2.tnodeWidth * Guts_H.YAFFS_NTNODES_LEVEL0)/8;
if (tn != null) {
if (level > 0) {
for (i = 0; i < Guts_H.YAFFS_NTNODES_INTERNAL && ok; i++){
if (tn.internal[i] != null) {
ok = yaffs_CheckpointTnodeWorker(in,
tn.internal[i],
level - 1,
(chunkOffset<<Guts_H.YAFFS_TNODES_INTERNAL_BITS) + i);
}
}
} else if (level == 0) {
/*__u32*/ byte[] baseOffset = new byte[4]; final int baseOffsetIndex = 0;
Utils.writeIntToByteArray(baseOffset, baseOffsetIndex,
chunkOffset << Guts_H.YAFFS_TNODES_LEVEL0_BITS);
/* printf("write tnode at %d\n",baseOffset); */
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,baseOffset,baseOffsetIndex,4) == 4);
if(ok)
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,tn.serialized,tn.offset,nTnodeBytes) == nTnodeBytes);
}
}
return ok;
}
static boolean yaffs_WriteCheckpointTnodes(yaffs_Object obj)
{
/**__u32*/ byte[] endMarker = new byte[Constants.SIZEOF_INT]; final int endMarkerIndex = 0;
Utils.writeIntToByteArray(endMarker, endMarkerIndex, ~0);
boolean ok = true;
if(obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE){
ok = yaffs_CheckpointTnodeWorker(obj,
obj.variant.fileVariant().top,
obj.variant.fileVariant().topLevel,
0);
if(ok)
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(obj.myDev,endMarker,endMarkerIndex,Constants.SIZEOF_INT) ==
Constants.SIZEOF_INT);
}
return ok ? true : false;
}
static boolean yaffs_ReadCheckpointTnodes(yaffs_Object obj)
{
/**__u32*/ byte[] baseChunk = new byte[Constants.SIZEOF_INT];
final int baseChunkIndex = 0;
boolean ok = true;
yaffs_Device dev = obj.myDev;
yaffs_FileStructure fileStructPtr = obj.variant.fileVariant();
yaffs_Tnode tn;
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,baseChunk,baseChunkIndex,Constants.SIZEOF_INT) == Constants.SIZEOF_INT);
while(ok && (~Utils.getIntFromByteArray(baseChunk, baseChunkIndex)) != 0){
/* Read level 0 tnode */
/* printf("read tnode at %d\n",baseChunk); */
tn = yaffs_GetTnodeRaw(dev);
if(tn != null)
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,tn.serialized,tn.offset,
(dev.subField2.tnodeWidth * Guts_H.YAFFS_NTNODES_LEVEL0)/8) ==
(dev.subField2.tnodeWidth * Guts_H.YAFFS_NTNODES_LEVEL0)/8);
else
ok = false;
if(tn != null && ok){
ok = yaffs_AddOrFindLevel0Tnode(dev,
fileStructPtr,
Utils.getIntFromByteArray(baseChunk, baseChunkIndex),
tn) != null ? true : false;
}
if(ok)
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,baseChunk,baseChunkIndex,
Constants.SIZEOF_INT) ==
Constants.SIZEOF_INT);
}
return ok ? true : false;
}
static boolean yaffs_WriteCheckpointObjects(yaffs_Device dev)
{
yaffs_Object obj;
yaffs_CheckpointObject cp = new yaffs_CheckpointObject();
int i;
boolean ok = true;
list_head lh;
/* Iterate through the objects in each hash entry,
* dumping them to the checkpointing stream.
*/
for(i = 0; ok && i < Guts_H.YAFFS_NOBJECT_BUCKETS; i++){
// list_for_each(lh, &dev.objectBucket[i].list) {
for (lh = dev.subField3.objectBucket[i].list.next(); lh != dev.subField3.objectBucket[i].list;
lh = lh.next()) {
if (lh != null) {
obj = /*list_entry(lh, yaffs_Object, hashLink)*/ (yaffs_Object)lh.list_entry;
if (!obj.deferedFree) {
Unix.memset(cp.serialized,0,(byte)0,cp.getSerializedLength());
yaffs_ObjectToCheckpointObject(cp,obj);
cp.setStructType(yaffs_CheckpointObject.SERIALIZED_LENGTH);
yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,
("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(cp.objectId()),PrimitiveWrapperFactory.get(cp.parentId()),
PrimitiveWrapperFactory.get(cp.variantType()),PrimitiveWrapperFactory.get(cp.chunkId()),
/*(unsigned)*/PrimitiveWrapperFactory.get(yaffs2.utils.Utils.hashCode(obj)),
null, null, null, null);
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,cp.serialized,cp.offset,
yaffs_CheckpointObject.SERIALIZED_LENGTH)) == yaffs_CheckpointObject.SERIALIZED_LENGTH;
if(ok && obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE){
ok = yaffs_WriteCheckpointTnodes(obj);
}
}
}
}
}
/* Dump end of list */
Unix.memset(cp,(byte)0xFF/*,sizeof(yaffs_CheckpointObject)*/);
cp.setStructType(yaffs_CheckpointObject.SERIALIZED_LENGTH);
if(ok)
ok = (yaffs_checkptrw_C.yaffs_CheckpointWrite(dev,cp.serialized,cp.offset,
yaffs_CheckpointObject.SERIALIZED_LENGTH) == yaffs_CheckpointObject.SERIALIZED_LENGTH);
return ok ? true : false;
}
static boolean yaffs_ReadCheckpointObjects(yaffs_Device dev)
{
yaffs_Object obj;
yaffs_CheckpointObject cp = new yaffs_CheckpointObject();
boolean ok = true;
boolean done = false;
yaffs_Object hardList = null;
while(ok && !done) {
ok = (yaffs_checkptrw_C.yaffs_CheckpointRead(dev,cp.serialized,cp.offset,
yaffs_CheckpointObject.SERIALIZED_LENGTH) == yaffs_CheckpointObject.SERIALIZED_LENGTH);
if(cp.structType() != yaffs_CheckpointObject.SERIALIZED_LENGTH) {
/* printf("structure parsing failed\n"); */
ok = false;
}
if(ok && cp.objectId() == ~0)
done = true;
else if(ok){
obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId(), cp.variantType());
yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("Checkpoint read object %d parent %d type %d chunk %d obj addr %x" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(cp.objectId()),PrimitiveWrapperFactory.get(cp.parentId()),
PrimitiveWrapperFactory.get(cp.variantType()),PrimitiveWrapperFactory.get(cp.chunkId()),
/*(unsigned)*/PrimitiveWrapperFactory.get(yaffs2.utils.Utils.hashCode(obj)),
null, null, null, null);
if(obj != null) {
yaffs_CheckpointObjectToObject(obj,cp);
if(obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE) {
ok = yaffs_ReadCheckpointTnodes(obj);
} else if(obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_HARDLINK) {
obj.hardLinks.next =
/*(list_head)*/
hardList;
hardList = obj;
}
}
}
}
if(ok)
yaffs_HardlinkFixup(dev,hardList);
return ok ? true : false;
}
static boolean yaffs_WriteCheckpointData(yaffs_Device dev)
{
boolean ok;
ok = yaffs_checkptrw_C.yaffs_CheckpointOpen(dev,true) ? true : false;
if(ok)
ok = yaffs_WriteCheckpointValidityMarker(dev,1);
if(ok)
ok = yaffs_WriteCheckpointDevice(dev) ? true : false;
if(ok)
ok = yaffs_WriteCheckpointObjects(dev) ? true : false;
if(ok)
ok = yaffs_WriteCheckpointValidityMarker(dev,0);
if(!yaffs_checkptrw_C.yaffs_CheckpointClose(dev))
ok = false;
if(ok)
dev.subField2.isCheckpointed = true;
else
dev.subField2.isCheckpointed = false;
return dev.subField2.isCheckpointed;
}
static boolean yaffs_ReadCheckpointData(yaffs_Device dev)
{
boolean ok;
ok = yaffs_checkptrw_C.yaffs_CheckpointOpen(dev,false); /* open for read */
if(ok)
ok = yaffs_ReadCheckpointValidityMarker(dev,1);
if(ok)
ok = yaffs_ReadCheckpointDevice(dev);
if(ok)
ok = yaffs_ReadCheckpointObjects(dev);
if(ok)
ok = yaffs_ReadCheckpointValidityMarker(dev,0);
if(!yaffs_checkptrw_C.yaffs_CheckpointClose(dev))
ok = false;
if(ok)
dev.subField2.isCheckpointed = true;
else
dev.subField2.isCheckpointed = false;
return ok ? true : false;
}
static void yaffs_InvalidateCheckpoint(yaffs_Device dev)
{
if(dev.subField2.isCheckpointed ||
dev.subField2.blocksInCheckpoint > 0){
dev.subField2.isCheckpointed = false;
yaffs_checkptrw_C.yaffs_CheckpointInvalidateStream(dev);
if(dev.subField1.superBlock != null && dev.subField1.markSuperBlockDirty != null)
dev.subField1.markSuperBlockDirty.markSuperBlockDirty(dev.subField1.superBlock);
}
}
static boolean yaffs_CheckpointSave(yaffs_Device dev)
{
yaffs_ReportOddballBlocks(dev);
yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("save entry: isCheckpointed %b"+ ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(dev.subField2.isCheckpointed));
if(!dev.subField2.isCheckpointed)
yaffs_WriteCheckpointData(dev);
yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("save exit: isCheckpointed %b"+ ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(dev.subField2.isCheckpointed));
return dev.subField2.isCheckpointed;
}
static boolean yaffs_CheckpointRestore(yaffs_Device dev)
{
boolean retval;
yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("restore entry: isCheckpointed %b"+ ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(dev.subField2.isCheckpointed));
retval = yaffs_ReadCheckpointData(dev);
yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("restore exit: isCheckpointed %b"+ ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(dev.subField2.isCheckpointed));
yaffs_ReportOddballBlocks(dev);
return retval;
}
/*--------------------- File read/write ------------------------
* Read and write have very similar structures.
* In general the read/write has three parts to it
* An incomplete chunk to start with (if the read/write is not chunk-aligned)
* Some complete chunks
* An incomplete chunk to end off with
*
* Curve-balls: the first chunk might also be the last chunk.
*/
static int yaffs_ReadDataFromFile(yaffs_Object in, /*__u8 **/ byte[] buffer, int bufferIndex,
/*loff_t*/ int offset,
int nBytes)
{
int chunk;
int start;
int nToCopy;
int n = nBytes;
int nDone = 0;
yaffs_ChunkCache cache;
yaffs_Device dev;
dev = in.myDev;
while (n > 0) {
//chunk = offset / dev.nDataBytesPerChunk + 1;
//start = offset % dev.nDataBytesPerChunk;
IntegerPointer chunkPointer = new IntegerPointer();
IntegerPointer startPointer = new IntegerPointer();
yaffs_AddrToChunk(dev,offset,chunkPointer,startPointer);
chunk = chunkPointer.dereferenced;
start = startPointer.dereferenced;
chunk++;
/* OK now check for the curveball where the start and end are in
* the same chunk.
*/
if ((start + n) < dev.subField1.nDataBytesPerChunk) {
nToCopy = n;
} else {
nToCopy = dev.subField1.nDataBytesPerChunk - start;
}
cache = yaffs_FindChunkCache(in, chunk);
/* If the chunk is already in the cache or it is less than a whole chunk
* then use the cache (if there is caching)
* else bypass the cache.
*/
if (cache != null || nToCopy != dev.subField1.nDataBytesPerChunk) {
if (dev.subField1.nShortOpCaches > 0) {
/* If we can't find the data in the cache, then load it up. */
if (!(cache != null)) {
cache = yaffs_GrabChunkCache(in.myDev);
cache.object = in;
cache.chunkId = chunk;
cache.dirty = false;
cache.locked = false;
yaffs_ReadChunkDataFromObject(in, chunk,
cache.data, cache.dataIndex);
cache.nBytes = 0;
}
yaffs_UseChunkCache(dev, cache, false);
cache.locked = true;
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_UnlockYAFFS(TRUE);
// #endif
Unix.memcpy(buffer, bufferIndex, cache.data, cache.dataIndex+start, nToCopy);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_LockYAFFS(TRUE);
// #endif
cache.locked = false;
} else {
/* Read into the local buffer then copy..*/
/*__u8 **/ byte[] localBuffer =
yaffs_GetTempBuffer(dev, 17 /*Utils.__LINE__()*/);
final int localBufferIndex = 0;
yaffs_ReadChunkDataFromObject(in, chunk,
localBuffer, localBufferIndex);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_UnlockYAFFS(TRUE);
// #endif
Unix.memcpy(buffer, bufferIndex, localBuffer, localBufferIndex+start, nToCopy);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_LockYAFFS(TRUE);
// #endif
yaffs_ReleaseTempBuffer(dev, localBuffer,
18 /*Utils.__LINE__()*/);
}
} else {
// #ifdef CONFIG_YAFFS_WINCE
// __u8 *localBuffer = yaffs_GetTempBuffer(dev, Utils.__LINE__);
// /* Under WinCE can't do direct transfer. Need to use a local buffer.
// * This is because we otherwise screw up WinCE's memory mapper
// */
// yaffs_ReadChunkDataFromObject(in, chunk, localBuffer);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_UnlockYAFFS(TRUE);
// #endif
// Unix.memcpy(buffer, localBuffer, dev.nDataBytesPerChunk);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_LockYAFFS(TRUE);
// yaffs_ReleaseTempBuffer(dev, localBuffer, Utils.__LINE__);
// #endif
// #else
/* A full chunk. Read directly into the supplied buffer. */
yaffs_ReadChunkDataFromObject(in, chunk, buffer, bufferIndex);
// #endif
}
n -= nToCopy;
offset += nToCopy;
bufferIndex += nToCopy;
nDone += nToCopy;
}
return nDone;
}
static int yaffs_WriteDataToFile(yaffs_Object in, /*const __u8 **/ byte[] buffer,
int bufferIndex, /*loff_t*/ int offset,
int nBytes, boolean writeThrough)
{
int chunk;
int start;
int nToCopy;
int n = nBytes;
int nDone = 0;
int nToWriteBack;
int startOfWrite = offset;
int chunkWritten = 0;
int nBytesRead;
yaffs_Device dev;
dev = in.myDev;
while (n > 0 && chunkWritten >= 0) {
//chunk = offset / dev.nDataBytesPerChunk + 1;
//start = offset % dev.nDataBytesPerChunk;
IntegerPointer chunkPointer = new IntegerPointer();
IntegerPointer startPointer = new IntegerPointer();
yaffs_AddrToChunk(dev,offset,chunkPointer,startPointer);
chunk = chunkPointer.dereferenced;
start = startPointer.dereferenced;
chunk++;
/* OK now check for the curveball where the start and end are in
* the same chunk.
*/
if ((start + n) < dev.subField1.nDataBytesPerChunk) {
nToCopy = n;
/* Now folks, to calculate how many bytes to write back....
* If we're overwriting and not writing to then end of file then
* we need to write back as much as was there before.
*/
nBytesRead =
in.variant.fileVariant().fileSize -
((chunk - 1) * dev.subField1.nDataBytesPerChunk);
if (nBytesRead > dev.subField1.nDataBytesPerChunk) {
nBytesRead = dev.subField1.nDataBytesPerChunk;
}
nToWriteBack =
(nBytesRead >
(start + n)) ? nBytesRead : (start + n);
} else {
nToCopy = dev.subField1.nDataBytesPerChunk - start;
nToWriteBack = dev.subField1.nDataBytesPerChunk;
}
if (nToCopy != dev.subField1.nDataBytesPerChunk) {
/* An incomplete start or end chunk (or maybe both start and end chunk) */
if (dev.subField1.nShortOpCaches > 0) {
yaffs_ChunkCache cache;
/* If we can't find the data in the cache, then load the cache */
cache = yaffs_FindChunkCache(in, chunk);
if (!(cache != null)
&& yaffs_CheckSpaceForAllocation(in.
myDev)) {
cache = yaffs_GrabChunkCache(in.myDev);
cache.object = in;
cache.chunkId = chunk;
cache.dirty = false;
cache.locked = false;
yaffs_ReadChunkDataFromObject(in, chunk,
cache.data, cache.dataIndex);
}
else if(cache != null &&
!cache.dirty &&
!yaffs_CheckSpaceForAllocation(in.myDev)){
/* Drop the cache if it was a read cache item and
* no space check has been made for it.
*/
cache = null;
}
if (cache != null) {
yaffs_UseChunkCache(dev, cache, true);
cache.locked = true;
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_UnlockYAFFS(TRUE);
// #endif
Unix.memcpy(cache.data, cache.dataIndex+start, buffer, bufferIndex,
nToCopy);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_LockYAFFS(TRUE);
// #endif
cache.locked = false;
cache.nBytes = nToWriteBack;
if (writeThrough) {
chunkWritten =
yaffs_WriteChunkDataToObject
(cache.object,
cache.chunkId,
cache.data, cache.dataIndex, cache.nBytes,
true);
cache.dirty = false;
}
} else {
chunkWritten = -1; /* fail the write */
}
} else {
/* An incomplete start or end chunk (or maybe both start and end chunk)
* Read into the local buffer then copy, then copy over and write back.
*/
/*__u8 **/ byte[] localBuffer =
yaffs_GetTempBuffer(dev, 19 /*Utils.__LINE__()*/);
final int localBufferIndex = 0;
yaffs_ReadChunkDataFromObject(in, chunk,
localBuffer, localBufferIndex);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_UnlockYAFFS(TRUE);
// #endif
Unix.memcpy(localBuffer, start, buffer, bufferIndex, nToCopy);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_LockYAFFS(TRUE);
// #endif
chunkWritten =
yaffs_WriteChunkDataToObject(in, chunk,
localBuffer, localBufferIndex,
nToWriteBack,
false);
yaffs_ReleaseTempBuffer(dev, localBuffer,
20 /*Utils.__LINE__()*/);
}
} else {
// #ifdef CONFIG_YAFFS_WINCE
// /* Under WinCE can't do direct transfer. Need to use a local buffer.
// * This is because we otherwise screw up WinCE's memory mapper
// */
// __u8 *localBuffer = yaffs_GetTempBuffer(dev, Utils.__LINE__);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_UnlockYAFFS(TRUE);
// #endif
// Unix.memcpy(localBuffer, buffer, dev.nDataBytesPerChunk);
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_LockYAFFS(TRUE);
// #endif
// chunkWritten =
// yaffs_WriteChunkDataToObject(in, chunk, localBuffer,
// dev.nDataBytesPerChunk,
// 0);
// yaffs_ReleaseTempBuffer(dev, localBuffer, Utils.__LINE__);
// #else
/* A full chunk. Write directly from the supplied buffer. */
chunkWritten =
yaffs_WriteChunkDataToObject(in, chunk, buffer, bufferIndex,
dev.subField1.nDataBytesPerChunk,
false);
// #endif
/* Since we've overwritten the cached data, we better invalidate it. */
yaffs_InvalidateChunkCache(in, chunk);
}
if (chunkWritten >= 0) {
n -= nToCopy;
offset += nToCopy;
bufferIndex += nToCopy;
nDone += nToCopy;
}
}
/* Update file object */
if ((startOfWrite + nDone) > in.variant.fileVariant().fileSize) {
in.variant.fileVariant().fileSize = (startOfWrite + nDone);
}
in.dirty = true;
return nDone;
}
/* ---------------------- File resizing stuff ------------------ */
static void yaffs_PruneResizedChunks(yaffs_Object in, int newSize)
{
yaffs_Device dev = in.myDev;
int oldFileSize = in.variant.fileVariant().fileSize;
int lastDel = 1 + (oldFileSize - 1) / dev.subField1.nDataBytesPerChunk;
int startDel = 1 + (newSize + dev.subField1.nDataBytesPerChunk - 1) /
dev.subField1.nDataBytesPerChunk;
int i;
int chunkId;
/* Delete backwards so that we don't end up with holes if
* power is lost part-way through the operation.
*/
for (i = lastDel; i >= startDel; i--) {
/* NB this could be optimised somewhat,
* eg. could retrieve the tags and write them without
* using yaffs_DeleteChunk
*/
chunkId = yaffs_FindAndDeleteChunkInFile(in, i, null);
if (chunkId > 0) {
if (chunkId <
(dev.subField2.internalStartBlock * dev.subField1.nChunksPerBlock)
|| chunkId >=
((dev.subField2.internalEndBlock +
1) * dev.subField1.nChunksPerBlock)) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("Found daft chunkId %d for %d" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(chunkId), PrimitiveWrapperFactory.get(i));
} else {
in.nDataChunks--;
yaffs_DeleteChunk(dev, chunkId, true, 21 /*Utils.__LINE__()*/);
}
}
}
}
static int yaffs_ResizeFile(yaffs_Object in, /*loff_t*/ int newSize)
{
int oldFileSize = in.variant.fileVariant().fileSize;
int newSizeOfPartialChunk;
int newFullChunks;
yaffs_Device dev = in.myDev;
IntegerPointer newFullChunksPointer = new IntegerPointer();
IntegerPointer newSizeOfPartialChunkPointer = new IntegerPointer();
yaffs_AddrToChunk(dev, newSize, newFullChunksPointer, newSizeOfPartialChunkPointer);
newFullChunks = newFullChunksPointer.dereferenced;
newSizeOfPartialChunk = newSizeOfPartialChunkPointer.dereferenced;
yaffs_FlushFilesChunkCache(in);
yaffs_InvalidateWholeChunkCache(in);
yaffs_CheckGarbageCollection(dev);
if (in.variantType != Guts_H.YAFFS_OBJECT_TYPE_FILE) {
return yaffs_GetFileSize(in);
}
if (newSize == oldFileSize) {
return oldFileSize;
}
if (newSize < oldFileSize) {
yaffs_PruneResizedChunks(in, newSize);
if (newSizeOfPartialChunk != 0) {
int lastChunk = 1 + newFullChunks;
/*__u8 **/ byte[] localBuffer = yaffs_GetTempBuffer(dev, 22 /*Utils.__LINE__()*/);
final int localBufferIndex = 0;
/* Got to read and rewrite the last chunk with its new size and zero pad */
yaffs_ReadChunkDataFromObject(in, lastChunk,
localBuffer, localBufferIndex);
Unix.memset(localBuffer, localBufferIndex+newSizeOfPartialChunk, (byte)0,
dev.subField1.nDataBytesPerChunk - newSizeOfPartialChunk);
yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer, localBufferIndex,
newSizeOfPartialChunk, true);
yaffs_ReleaseTempBuffer(dev, localBuffer, 23 /*Utils.__LINE__()*/);
}
in.variant.fileVariant().fileSize = newSize;
yaffs_PruneFileStructure(dev, in.variant.fileVariant());
} else {
/* newsSize > oldFileSize */
in.variant.fileVariant().fileSize = newSize;
}
/* Write a new object header.
* show we've shrunk the file, if need be
* Do this only if the file is not in the deleted directories.
*/
if (in.parent.objectId != Guts_H.YAFFS_OBJECTID_UNLINKED &&
in.parent.objectId != Guts_H.YAFFS_OBJECTID_DELETED) {
yaffs_UpdateObjectHeader(in, null, 0, false,
(newSize < oldFileSize) ? true : false, 0);
}
return newSize;
}
static /*loff_t*/ int yaffs_GetFileSize(yaffs_Object obj)
{
obj = yaffs_GetEquivalentObject(obj);
switch (obj.variantType) {
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
return obj.variant.fileVariant().fileSize;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
return ydirectenv.yaffs_strlen(obj.variant.symLinkVariant().alias,
obj.variant.symLinkVariant().aliasIndex);
default:
return 0;
}
}
static boolean yaffs_FlushFile(yaffs_Object in, boolean updateTime)
{
boolean retVal;
if (in.dirty) {
yaffs_FlushFilesChunkCache(in);
if (updateTime) {
// #ifdef CONFIG_YAFFS_WINCE
// yfsd_WinFileTimeNow(in.win_mtime);
// #else
in.yst_mtime = ydirectenv.Y_CURRENT_TIME();
// #endif
}
retVal =
(yaffs_UpdateObjectHeader(in, null, 0, false, false, 0) >=
0) ? Guts_H.YAFFS_OK : Guts_H.YAFFS_FAIL;
} else {
retVal = Guts_H.YAFFS_OK;
}
return retVal;
}
static boolean yaffs_DoGenericObjectDeletion(yaffs_Object in)
{
/* First off, invalidate the file's data in the cache, without flushing. */
yaffs_InvalidateWholeChunkCache(in);
if (in.myDev.subField1.isYaffs2 && (in.parent != in.myDev.deletedDir)) {
/* Move to the unlinked directory so we have a record that it was deleted. */
yaffs_ChangeObjectName(in, in.myDev.deletedDir, null, 0, false, 0);
}
yaffs_RemoveObjectFromDirectory(in);
yaffs_DeleteChunk(in.myDev, in.chunkId, true, 24 /*Utils.__LINE__()*/);
in.chunkId = -1;
yaffs_FreeObject(in);
return Guts_H.YAFFS_OK;
}
/* yaffs_DeleteFile deletes the whole file data
* and the inode associated with the file.
* It does not delete the links associated with the file.
*/
static boolean yaffs_UnlinkFile(yaffs_Object in)
{
boolean retVal;
boolean immediateDeletion = false;
if (true) {
// #ifdef __KERNEL__
// if (!in.myInode) {
// immediateDeletion = 1;
// }
// #else
if (in.inUse <= 0) {
immediateDeletion = true;
}
// #endif
if (immediateDeletion) {
retVal =
yaffs_ChangeObjectName(in, in.myDev.deletedDir,
null, 0, false, 0);
yportenv.T(yportenv.YAFFS_TRACE_TRACING,
("yaffs: immediate deletion of file %d" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(in.objectId));
in.sub.deleted = true;
in.myDev.nDeletedFiles++;
if (false && in.myDev.subField1.isYaffs2) {
yaffs_ResizeFile(in, 0);
}
yaffs_SoftDeleteFile(in);
} else {
retVal =
yaffs_ChangeObjectName(in, in.myDev.unlinkedDir,
null, 0, false, 0);
}
}
return retVal;
}
static boolean yaffs_DeleteFile(yaffs_Object in)
{
boolean retVal = Guts_H.YAFFS_OK;
if (in.nDataChunks > 0) {
/* Use soft deletion if there is data in the file */
if (!in.sub.unlinked) {
retVal = yaffs_UnlinkFile(in);
}
if (retVal == Guts_H.YAFFS_OK && in.sub.unlinked && !in.sub.deleted) {
in.sub.deleted = true;
in.myDev.nDeletedFiles++;
yaffs_SoftDeleteFile(in);
}
return in.sub.deleted ? Guts_H.YAFFS_OK : Guts_H.YAFFS_FAIL;
} else {
/* The file has no data chunks so we toss it immediately */
yaffs_FreeTnode(in.myDev, in.variant.fileVariant().top);
in.variant.fileVariant().top = null;
yaffs_DoGenericObjectDeletion(in);
return Guts_H.YAFFS_OK;
}
}
static boolean yaffs_DeleteDirectory(yaffs_Object in)
{
/* First check that the directory is empty. */
if (devextras.list_empty(in.variant.directoryVariant().children)) {
return yaffs_DoGenericObjectDeletion(in);
}
return Guts_H.YAFFS_FAIL;
}
static boolean yaffs_DeleteSymLink(yaffs_Object in)
{
ydirectenv.YFREE(in.variant.symLinkVariant().alias);
return yaffs_DoGenericObjectDeletion(in);
}
static boolean yaffs_DeleteHardLink(yaffs_Object in)
{
/* remove this hardlink from the list assocaited with the equivalent
* object
*/
devextras.list_del(in.hardLinks);
return yaffs_DoGenericObjectDeletion(in);
}
static void yaffs_DestroyObject(yaffs_Object obj)
{
switch (obj.variantType) {
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
yaffs_DeleteFile(obj);
break;
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
yaffs_DeleteDirectory(obj);
break;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
yaffs_DeleteSymLink(obj);
break;
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
yaffs_DeleteHardLink(obj);
break;
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
yaffs_DoGenericObjectDeletion(obj);
break;
case Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN:
break; /* should not happen. */
}
}
static boolean yaffs_UnlinkWorker(yaffs_Object obj)
{
if (obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_HARDLINK) {
return yaffs_DeleteHardLink(obj);
} else if (!devextras.list_empty(obj.hardLinks)) {
/* Curve ball: We're unlinking an object that has a hardlink.
*
* This problem arises because we are not strictly following
* The Linux link/inode model.
*
* We can't really delete the object.
* Instead, we do the following:
* - Select a hardlink.
* - Unhook it from the hard links
* - Unhook it from its parent directory (so that the rename can work)
* - Rename the object to the hardlink's name.
* - Delete the hardlink
*/
yaffs_Object hl;
boolean retVal;
/*YCHAR*/ byte[] name = new byte[Guts_H.YAFFS_MAX_NAME_LENGTH + 1];
final int nameIndex = 0;
hl = /*list_entry(obj.hardLinks.next, yaffs_Object, hardLinks)*/ (yaffs_Object)obj.parent;
devextras.list_del_init(hl.hardLinks);
devextras.list_del_init(hl.siblings);
yaffs_GetObjectName(hl, name, nameIndex, Guts_H.YAFFS_MAX_NAME_LENGTH + 1);
retVal = yaffs_ChangeObjectName(obj, hl.parent, name, nameIndex, false, 0);
if (retVal == Guts_H.YAFFS_OK) {
retVal = yaffs_DoGenericObjectDeletion(hl);
}
return retVal;
} else {
switch (obj.variantType) {
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
return yaffs_UnlinkFile(obj);
// break;
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
return yaffs_DeleteDirectory(obj);
// break;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
return yaffs_DeleteSymLink(obj);
// break;
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
return yaffs_DoGenericObjectDeletion(obj);
// break;
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
case Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN:
default:
return Guts_H.YAFFS_FAIL;
}
}
}
static boolean yaffs_UnlinkObject( yaffs_Object obj)
{
if (obj != null && obj.unlinkAllowed) {
return yaffs_UnlinkWorker(obj);
}
return Guts_H.YAFFS_FAIL;
}
static boolean yaffs_Unlink(yaffs_Object dir, /*const YCHAR **/ byte[] name,
int nameIndex)
{
yaffs_Object obj;
obj = yaffs_FindObjectByName(dir, name, nameIndex);
return yaffs_UnlinkObject(obj);
}
/*----------------------- Initialisation Scanning ---------------------- */
static void yaffs_HandleShadowedObject(yaffs_Device dev, int objId,
boolean backwardScanning)
{
yaffs_Object obj;
if (!backwardScanning) {
/* Handle YAFFS1 forward scanning case
* For YAFFS1 we always do the deletion
*/
} else {
/* Handle YAFFS2 case (backward scanning)
* If the shadowed object exists then ignore.
*/
if (yaffs_FindObjectByNumber(dev, objId) != null) {
return;
}
}
/* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc.
* We put it in unlinked dir to be cleaned up after the scanning
*/
obj =
yaffs_FindOrCreateObjectByNumber(dev, objId,
Guts_H.YAFFS_OBJECT_TYPE_FILE);
yaffs_AddObjectToDirectory(dev.unlinkedDir, obj);
obj.variant.fileVariant().shrinkSize = 0;
obj.valid = true; /* So that we don't read any other info for this file */
}
// typedef struct {
// int seq;
// int block;
// } yaffs_BlockIndex;
static void yaffs_HardlinkFixup(yaffs_Device dev, yaffs_Object hardList)
{
yaffs_Object hl;
yaffs_Object in;
while (hardList != null) {
hl = hardList;
hardList = (yaffs_Object) (hardList.hardLinks.next);
in = yaffs_FindObjectByNumber(dev,
hl.variant.hardLinkVariant().
equivalentObjectId);
if (in != null) {
/* Add the hardlink pointers */
hl.variant.hardLinkVariant().equivalentObject = in;
devextras.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;
devextras.INIT_LIST_HEAD(hl.hardLinks);
}
}
}
// XXX low priority - its only used for quicksort
// XXX i thinks its a SerializableObject
static int ybicmp(/*const void **/ Object a, /*const void **/ Object b){
/*register */ int aseq = ((yaffs_BlockIndex)a).seq;
/*register */ int bseq = ((yaffs_BlockIndex)b).seq;
/*register */ int ablock = ((yaffs_BlockIndex)a).block;
/*register */ int bblock = ((yaffs_BlockIndex)b).block;
if( aseq == bseq )
return ablock - bblock;
else
return aseq - bseq;
}
static yaffs_ExtendedTags tags = new yaffs_ExtendedTags();
static int blk;
static int blockIterator;
static int startIterator;
static int endIterator;
static int nBlocksToScan = 0;
static boolean result;
static int chunk;
static int c;
static int deleted;
static /*yaffs_BlockState*/ int state;
static yaffs_Object hardList = null;
static yaffs_Object hl;
static yaffs_BlockInfo bi;
static long sequenceNumber;
static yaffs_ObjectHeader oh;
static yaffs_Object in;
static yaffs_Object parent;
static yaffs_BlockIndex[] blockIndex = null;
static /*__u8 **/ byte[] chunkData;
static final int chunkDataIndex = 0;
static boolean yaffs_Scan(yaffs_Device dev)
{
nBlocksToScan = 0;
hardList = null;
blockIndex = null;
int nBlocks = dev.subField2.internalEndBlock - dev.subField2.internalStartBlock + 1;
if (dev.subField1.isYaffs2) {
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
("yaffs_Scan is not for YAFFS2!" + ydirectenv.TENDSTR));
return Guts_H.YAFFS_FAIL;
}
//TODO Throw all the yaffs2 stuuf out of yaffs_Scan since it is only for yaffs1 format.
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
("yaffs_Scan starts intstartblk %d intendblk %d..." + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(dev.subField2.internalStartBlock), PrimitiveWrapperFactory.get(dev.subField2.internalEndBlock));
chunkData = yaffs_GetTempBuffer(dev, 25 /*Utils.__LINE__()*/);
// chunkDataIndex = 0;
dev.sequenceNumber = Guts_H.YAFFS_LOWEST_SEQUENCE_NUMBER;
if (dev.subField1.isYaffs2) {
blockIndex = ydirectenv.YMALLOC_BLOCKINDEX(nBlocks/* * sizeof(yaffs_BlockIndex)*/ );
}
/* Scan all the blocks to determine their state */
for (blk = dev.subField2.internalStartBlock; blk <= dev.subField2.internalEndBlock; blk++) {
bi = Guts_H.yaffs_GetBlockInfo(dev, blk);
yaffs_ClearChunkBits(dev, blk);
bi.setPagesInUse(0);
bi.setSoftDeletions(0);
IntegerPointer statePointer = new IntegerPointer();
IntegerPointer sequenceNumberPointer = new IntegerPointer();
yaffs_nand_C.yaffs_QueryInitialBlockState(dev, blk, statePointer, sequenceNumberPointer);
state = statePointer.dereferenced;
sequenceNumber = sequenceNumberPointer.dereferenced;
bi.setBlockState(state);
bi.setSequenceNumber((int)sequenceNumber);
yportenv.T(yportenv.YAFFS_TRACE_SCAN_DEBUG,
("Block scanning block %d state %d seq %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk),
PrimitiveWrapperFactory.get(state), PrimitiveWrapperFactory.get((int)sequenceNumber));
if (state == Guts_H.YAFFS_BLOCK_STATE_DEAD) {
yportenv.T(yportenv.YAFFS_TRACE_BAD_BLOCKS,
("block %d is bad" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk));
} else if (state == Guts_H.YAFFS_BLOCK_STATE_EMPTY) {
yportenv.T(yportenv.YAFFS_TRACE_SCAN_DEBUG,
(("Block empty " + ydirectenv.TENDSTR)));
dev.subField3.nErasedBlocks++;
dev.subField3.nFreeChunks += dev.subField1.nChunksPerBlock;
} else if (state == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
/* Determine the highest sequence number */
if (dev.subField1.isYaffs2 &&
sequenceNumber >= Guts_H.YAFFS_LOWEST_SEQUENCE_NUMBER &&
sequenceNumber < Guts_H.YAFFS_HIGHEST_SEQUENCE_NUMBER) {
blockIndex[nBlocksToScan].seq = (int)sequenceNumber;
blockIndex[nBlocksToScan].block = blk;
nBlocksToScan++;
if (sequenceNumber >= dev.sequenceNumber) {
dev.sequenceNumber = sequenceNumber;
}
} else if (dev.subField1.isYaffs2) {
/* TODO: Nasty sequence number! */
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
("Block scanning block %d has bad sequence number %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk), PrimitiveWrapperFactory.get((int)sequenceNumber));
}
}
}
/* Sort the blocks
* Dungy old bubble sort for now...
*/
if (dev.subField1.isYaffs2) {
yaffs_BlockIndex temp = new yaffs_BlockIndex();
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. */
if (dev.subField1.isYaffs2) {
startIterator = 0;
endIterator = nBlocksToScan - 1;
yportenv.T(yportenv.YAFFS_TRACE_SCAN_DEBUG,
("%d blocks to be scanned" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(nBlocksToScan));
} else {
startIterator = dev.subField2.internalStartBlock;
endIterator = dev.subField2.internalEndBlock;
}
return yaffs_Scan_Sub1(dev);
}
static boolean yaffs_Scan_Sub1(yaffs_Device dev)
{
/* For each block.... */
for (blockIterator = startIterator; blockIterator <= endIterator;
blockIterator++) {
if (dev.subField1.isYaffs2) {
/* get the block to scan in the correct order */
blk = blockIndex[blockIterator].block;
} else {
blk = blockIterator;
}
bi = Guts_H.yaffs_GetBlockInfo(dev, blk);
state = bi.blockState();
deleted = 0;
/* For each chunk in each block that needs scanning....*/
for (c = 0; c < dev.subField1.nChunksPerBlock &&
state == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) {
/* Read the tags and decide what to do */
chunk = blk * dev.subField1.nChunksPerBlock + c;
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev, chunk, null, 0,
tags);
/* Let's have a good look at this chunk... */
if (!dev.subField1.isYaffs2 && tags.chunkDeleted) {
/* YAFFS1 only...
* A deleted chunk
*/
deleted++;
dev.subField3.nFreeChunks++;
/*yportenv.T((" %d %d deleted\n",blk,c)); */
} else 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 = Guts_H.YAFFS_BLOCK_STATE_EMPTY;
dev.subField3.nErasedBlocks++;
} else {
/* this is the block being allocated from */
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
(" Allocating from %d %d" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(blk), PrimitiveWrapperFactory.get(c));
state = Guts_H.YAFFS_BLOCK_STATE_ALLOCATING;
dev.subField3.allocationBlock = blk;
dev.subField3.allocationPage = c;
dev.subField3.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.subField1.isYaffs2
&& (dev.sequenceNumber !=
Utils.intAsUnsignedInt(bi.sequenceNumber()))) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("yaffs: Allocation block %d was not highest sequence id:" +
" block seq = %d, dev seq = %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk),PrimitiveWrapperFactory.get((int)bi.sequenceNumber()),PrimitiveWrapperFactory.get((int)dev.sequenceNumber));
}
}
dev.subField3.nFreeChunks += (dev.subField1.nChunksPerBlock - c);
} else if (tags.chunkId > 0) {
/* chunkId > 0 so it is a data chunk... */
/*unsigned int*/ int endpos;
yaffs_SetChunkBit(dev, blk, c);
bi.setPagesInUse(bi.pagesInUse()+1);
in = yaffs_FindOrCreateObjectByNumber(dev,
tags.objectId,
Guts_H.YAFFS_OBJECT_TYPE_FILE);
/* PutChunkIntoFile checks for a clash (two data chunks with
* the same chunkId).
*/
yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,
1);
endpos =
(tags.chunkId - 1) * dev.subField1.nDataBytesPerChunk +
tags.byteCount;
if (in.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE
&& in.variant.fileVariant().scannedFileSize <
endpos) {
in.variant.fileVariant().
scannedFileSize = endpos;
if (!dev.subField1.useHeaderFileSize) {
in.variant.fileVariant().
fileSize =
in.variant.fileVariant().
scannedFileSize;
}
}
/* yportenv.T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); */
} else {
yaffs_Scan_Sub11(dev);
}
}
if (state == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
/* If we got this far while scanning, then the block is fully allocated.*/
state = Guts_H.YAFFS_BLOCK_STATE_FULL;
}
bi.setBlockState(state);
/* Now let's see if it was dirty */
if (bi.pagesInUse() == 0 &&
!bi.hasShrinkHeader() &&
bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_FULL) {
yaffs_BlockBecameDirty(dev, blk);
}
}
if (blockIndex != null) {
ydirectenv.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.
*/
yaffs_HardlinkFixup(dev,hardList);
/* Handle the unlinked files. Since they were left in an unlinked state we should
* just delete them.
*/
{
list_head i;
list_head n;
yaffs_Object l;
/* Soft delete all the unlinked files */
i = dev.unlinkedDir.variant.directoryVariant().children.next();
n = i.next();
// list_for_each_safe(i, n,
// &dev.unlinkedDir.variant.directoryVariant().
// children) {
while (i != dev.unlinkedDir.variant.directoryVariant().children) {
if (i != null) {
l = /*list_entry(i, yaffs_Object, siblings)*/ (yaffs_Object)i.list_entry;
yaffs_DestroyObject(l);
}
i = n;
n = i.next();
}
}
yaffs_ReleaseTempBuffer(dev, chunkData, 26 /*Utils.__LINE__()*/);
yportenv.T(yportenv.YAFFS_TRACE_SCAN, (("yaffs_Scan ends" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_OK;
}
// begin
static void yaffs_Scan_Sub11(yaffs_Device dev)
{
/* chunkId == 0, so it is an ObjectHeader.
* Thus, we read in the object header and make the object
*/
yaffs_SetChunkBit(dev, blk, c);
bi.setPagesInUse(bi.pagesInUse()+1);
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev, chunk,
chunkData, chunkDataIndex,
null);
oh = /*(yaffs_ObjectHeader *) chunkData*/
new yaffs_ObjectHeader(chunkData, chunkDataIndex);
in = yaffs_FindObjectByNumber(dev,
tags.objectId);
if (in != null && in.variantType != oh.type()) {
/* This should not happen, but somehow
* Wev'e ended up with an objectId that has been reused but not yet
* deleted, and worse still it has changed type. Delete the old object.
*/
yaffs_DestroyObject(in);
in = null;
}
in = yaffs_FindOrCreateObjectByNumber(dev,
tags.objectId,
oh.type());
if (oh.shadowsObject() > 0) {
yaffs_HandleShadowedObject(dev,
oh.shadowsObject(),
false);
}
if (in.valid) {
/* We have already filled this one. We have a duplicate and need to resolve it. */
/*unsigned*/ int existingSerial = Utils.byteAsUnsignedByte(in.serial);
/*unsigned*/ int newSerial = tags.serialNumber;
if (dev.subField1.isYaffs2 ||
((existingSerial + 1) & 3) ==
newSerial) {
/* Use new one - destroy the exisiting one */
yaffs_DeleteChunk(dev,
in.chunkId,
true, 27 /*Utils.__LINE__()*/);
in.valid = false;
} else {
/* Use existing - destroy this one. */
yaffs_DeleteChunk(dev, chunk, true,
28 /*Utils.__LINE__()*/);
}
}
if (!in.valid &&
(tags.objectId == Guts_H.YAFFS_OBJECTID_ROOT ||
tags.objectId == Guts_H.YAFFS_OBJECTID_LOSTNFOUND)) {
/* We only load some info, don't fiddle with directory structure */
in.valid = true;
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 = true;
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;
yaffs_SetObjectName(in, oh.name(), oh.nameIndex());
in.dirty = false;
/* directory stuff...
* hook up to parent
*/
parent =
yaffs_FindOrCreateObjectByNumber
(dev, oh.parentObjectId(),
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY);
if (parent.variantType ==
Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN) {
/* Set up as a directory */
parent.variantType =
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY;
devextras.INIT_LIST_HEAD(parent.variant.
directoryVariant().
children);
} else if (parent.variantType !=
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY)
{
/* Hoosterman, another problem....
* We're trying to use a non-directory as a directory
*/
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("yaffs tragedy: attempting to use non-directory as" +
" a directory in scan. Put in lost+found."
+ ydirectenv.TENDSTR));
parent = dev.lostNFoundDir;
}
}
yaffs_AddObjectToDirectory(parent, in);
if (false && (parent == dev.deletedDir ||
parent == dev.unlinkedDir)) {
in.sub.deleted = true; /* If it is unlinked at start up then it wants deleting */
dev.nDeletedFiles++;
}
/* 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 Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN:
/* Todo got a problem */
break;
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
if (dev.subField1.isYaffs2
&& oh.isShrink()) {
/* Prune back the shrunken chunks */
yaffs_PruneResizedChunks
(in, oh.fileSize());
/* Mark the block as having a shrinkHeader */
bi.setHasShrinkHeader(true);
}
if (dev.subField1.useHeaderFileSize)
in.variant.fileVariant().
fileSize =
oh.fileSize();
break;
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
in.variant.hardLinkVariant().
equivalentObjectId =
oh.equivalentObjectId();
in.hardLinks.next =
/*(list_head)*/
hardList;
hardList = in;
break;
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
/* Do nothing */
break;
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
/* Do nothing */
break;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
// XXX PORT Dynamic memory allocation, but this is during scan.
in.variant.symLinkVariant().alias =
yaffs_CloneString(oh.alias(), oh.aliasIndex());
in.variant.symLinkVariant().aliasIndex = 0;
break;
}
if (parent == dev.deletedDir) {
yaffs_DestroyObject(in);
bi.setHasShrinkHeader(true);
}
}
// end
static void yaffs_CheckObjectDetailsLoaded(yaffs_Object in)
{
/*__u8 **/ byte[] chunkData; final int chunkDataIndex;
yaffs_ObjectHeader oh;
yaffs_Device dev = in.myDev;
yaffs_ExtendedTags tags = new yaffs_ExtendedTags();
boolean result;
// #if 0
// yportenv.T(yportenv.YAFFS_TRACE_SCAN,(("details for object %d %s loaded" + ydirectenv.TENDSTR),
// in.objectId,
// in.lazyLoaded ? "not yet" : "already"));
// #endif
if(in.lazyLoaded){
in.lazyLoaded = false;
chunkData = yaffs_GetTempBuffer(dev, 29 /*Utils.__LINE__()*/);
chunkDataIndex = 0;
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev,in.chunkId,chunkData,chunkDataIndex,tags);
oh = /*(yaffs_ObjectHeader *) chunkData*/ new yaffs_ObjectHeader(chunkData,chunkDataIndex);
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
yaffs_SetObjectName(in, oh.name(),oh.nameIndex());
if(in.variantType == Guts_H.YAFFS_OBJECT_TYPE_SYMLINK)
{
in.variant.symLinkVariant().alias =
yaffs_CloneString(oh.alias(),oh.aliasIndex());
in.variant.symLinkVariant().aliasIndex = 0;
}
yaffs_ReleaseTempBuffer(dev,chunkData, 30 /*Utils.__LINE__()*/);
}
}
static boolean itsUnlinked;
static int fileSize;
static boolean isShrink;
static boolean foundChunksInBlock;
static int equivalentObjectId;
static boolean altBlockIndex = false;
static boolean yaffs_ScanBackwards(yaffs_Device dev)
{
tags = new yaffs_ExtendedTags();
// int blk;
// int blockIterator;
// int startIterator;
// int endIterator;
nBlocksToScan = 0;
// int chunk;
// boolean result;
// int c;
// boolean deleted;
// /*yaffs_BlockState*/ int state;
hardList = null;
// yaffs_BlockInfo bi;
// long sequenceNumber;
// yaffs_ObjectHeader oh;
// yaffs_Object in;
// yaffs_Object parent;
int nBlocks = dev.subField2.internalEndBlock - dev.subField2.internalStartBlock + 1;
// boolean itsUnlinked;
// /*__u8 **/ byte[] chunkData;
// final int chunkDataIndex;
// int fileSize;
// boolean isShrink;
// boolean foundChunksInBlock;
// int equivalentObjectId;
blockIndex = null;
altBlockIndex = false;
if (!dev.subField1.isYaffs2) {
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
(("yaffs_ScanBackwards is only for YAFFS2!" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
("yaffs_ScanBackwards starts intstartblk %d intendblk %d..."
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField2.internalStartBlock), PrimitiveWrapperFactory.get(dev.subField2.internalEndBlock));
dev.sequenceNumber = Guts_H.YAFFS_LOWEST_SEQUENCE_NUMBER;
blockIndex = ydirectenv.YMALLOC_BLOCKINDEX(nBlocks/* * sizeof(yaffs_BlockIndex)*/);
if(!(blockIndex != null)) {
blockIndex = ydirectenv.YMALLOC_ALT_BLOCKINDEX(nBlocks/* * sizeof(yaffs_BlockIndex)*/);
altBlockIndex = true;
}
if(!(blockIndex != null)) {
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
(("yaffs_Scan() could not allocate block index!" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
chunkData = yaffs_GetTempBuffer(dev, 31 /*Utils.__LINE__()*/);
//chunkDataIndex = 0; XXX
/* Scan all the blocks to determine their state */
for (blk = dev.subField2.internalStartBlock; blk <= dev.subField2.internalEndBlock; blk++) {
bi = Guts_H.yaffs_GetBlockInfo(dev, blk);
yaffs_ClearChunkBits(dev, blk);
bi.setPagesInUse(0);
bi.setSoftDeletions(0);
IntegerPointer statePointer = new IntegerPointer();
IntegerPointer sequenceNumberPointer = new IntegerPointer();
yaffs_nand_C.yaffs_QueryInitialBlockState(dev, blk, statePointer, sequenceNumberPointer);
state = statePointer.dereferenced;
sequenceNumber = sequenceNumberPointer.dereferenced;
bi.setBlockState(state);
bi.setSequenceNumber((int)sequenceNumber);
if(bi.sequenceNumber() == Guts_H.YAFFS_SEQUENCE_CHECKPOINT_DATA)
bi.setBlockState(state = Guts_H.YAFFS_BLOCK_STATE_CHECKPOINT);
yportenv.T(yportenv.YAFFS_TRACE_SCAN_DEBUG,
("Block scanning block %d state %d seq %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk),
PrimitiveWrapperFactory.get(state), PrimitiveWrapperFactory.get((int)sequenceNumber));
if(state == Guts_H.YAFFS_BLOCK_STATE_CHECKPOINT){
/* todo .. fix free space ? */
} else if (state == Guts_H.YAFFS_BLOCK_STATE_DEAD) {
yportenv.T(yportenv.YAFFS_TRACE_BAD_BLOCKS,
("block %d is bad" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk));
} else if (state == Guts_H.YAFFS_BLOCK_STATE_EMPTY) {
yportenv.T(yportenv.YAFFS_TRACE_SCAN_DEBUG,
("Block empty " + ydirectenv.TENDSTR));
dev.subField3.nErasedBlocks++;
dev.subField3.nFreeChunks += dev.subField1.nChunksPerBlock;
} else if (state == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
/* Determine the highest sequence number */
if (dev.subField1.isYaffs2 &&
sequenceNumber >= Guts_H.YAFFS_LOWEST_SEQUENCE_NUMBER &&
sequenceNumber < Guts_H.YAFFS_HIGHEST_SEQUENCE_NUMBER) {
blockIndex[nBlocksToScan].seq = (int)sequenceNumber;
blockIndex[nBlocksToScan].block = blk;
nBlocksToScan++;
if (sequenceNumber >= dev.sequenceNumber) {
dev.sequenceNumber = sequenceNumber;
}
} else if (dev.subField1.isYaffs2) {
/* TODO: Nasty sequence number! */
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
("Block scanning block %d has bad sequence number %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk), PrimitiveWrapperFactory.get((int)sequenceNumber));
}
}
}
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
("%d blocks to be sorted..." + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(nBlocksToScan));
ydirectenv.YYIELD();
/* Sort the blocks */ // XXX use the qsort impl. taken from http://www.google.com/codesearch?hl=en&q=+lang:java+quicksort+show:GbJN4YS8fv4:bWk7RV6TTho:R2hTeVo7FHc&sa=N&cd=2&ct=rc&cs_p=http://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.8a3/src/mozilla-source-1.8a3.tar.bz2&cs_f=mozilla/gc/boehm/leaksoup/QuickSort.java#a0
// #ifndef CONFIG_YAFFS_USE_OWN_SORT
// {
// /* Use qsort now. */
// qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp);
// }
// #else
{
/* Dungy old bubble sort... */
yaffs_BlockIndex temp = new yaffs_BlockIndex();
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;
}
}
// #endif
ydirectenv.YYIELD();
yportenv.T(yportenv.YAFFS_TRACE_SCAN, ("...done" + ydirectenv.TENDSTR));
/* Now scan the blocks looking at the data. */
startIterator = 0;
endIterator = nBlocksToScan - 1;
yportenv.T(yportenv.YAFFS_TRACE_SCAN_DEBUG,
("%d blocks to be scanned" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(nBlocksToScan));
return yaffs_ScanBackward_Sub0(dev);
}
static boolean yaffs_ScanBackward_Sub0(yaffs_Device dev)
{
/* For each block.... backwards */
for (blockIterator = endIterator; blockIterator >= startIterator;
blockIterator--) {
/* Cooperative multitasking! This loop can run for so
long that watchdog timers expire. */
ydirectenv.YYIELD();
/* get the block to scan in the correct order */
blk = blockIndex[blockIterator].block;
bi = Guts_H.yaffs_GetBlockInfo(dev, blk);
state = bi.blockState();
deleted = 0;
/* For each chunk in each block that needs scanning.... */
foundChunksInBlock = false;
for (c = dev.subField1.nChunksPerBlock - 1; c >= 0 &&
(state == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
state == Guts_H.YAFFS_BLOCK_STATE_ALLOCATING); c--) {
/* Scan backwards...
* Read the tags and decide what to do
*/
chunk = blk * dev.subField1.nChunksPerBlock + c;
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev, chunk, null, 0,
tags);
/* Let's have a good look at this chunk... */
if (!tags.chunkUsed) {
/* An unassigned chunk in the block.
* If there are used chunks after this one, then
* it is a chunk that was skipped due to failing the erased
* check. Just skip it so that it can be deleted.
* But, more typically, We get here when this is an unallocated
* chunk and his means that either the block is empty or
* this is the one being allocated from
*/
if(foundChunksInBlock)
{
/* This is a chunk that was skipped due to failing the erased check */
} else if (c == 0) {
/* We're looking at the first chunk in the block so the block is unused */
state = Guts_H.YAFFS_BLOCK_STATE_EMPTY;
dev.subField3.nErasedBlocks++;
} else {
if (state == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
state == Guts_H.YAFFS_BLOCK_STATE_ALLOCATING) {
if(dev.sequenceNumber == Utils.intAsUnsignedInt(bi.sequenceNumber())) {
/* this is the block being allocated from */
yportenv.T(yportenv.YAFFS_TRACE_SCAN,
(" Allocating from %d %d"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blk), PrimitiveWrapperFactory.get(c));
state = Guts_H.YAFFS_BLOCK_STATE_ALLOCATING;
dev.subField3.allocationBlock = blk;
dev.subField3.allocationPage = c;
dev.subField3.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.
*/
bi.setNeedsRetiring(true);
bi.setGcPrioritise(true);
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("Partially written block %d being set for retirement" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(blk));
}
}
}
dev.subField3.nFreeChunks++;
} else if (tags.chunkId > 0) {
/* chunkId > 0 so it is a data chunk... */
/*unsigned int*/ int endpos;
/*__u32*/ int chunkBase =
(tags.chunkId - 1) * dev.subField1.nDataBytesPerChunk;
foundChunksInBlock = true;
yaffs_SetChunkBit(dev, blk, c);
bi.setPagesInUse(bi.pagesInUse()+1);
in = yaffs_FindOrCreateObjectByNumber(dev,
tags.
objectId,
Guts_H.YAFFS_OBJECT_TYPE_FILE);
if (in.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE
&& chunkBase <
Utils.intAsUnsignedInt(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.subField1.nDataBytesPerChunk +
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, true, 32 /*Utils.__LINE__()*/);
}
} else {
/* chunkId == 0, so it is an ObjectHeader.
* Thus, we read in the object header and make the object
*/
foundChunksInBlock = true;
yaffs_SetChunkBit(dev, blk, c);
bi.setPagesInUse(bi.pagesInUse()+1);
oh = null;
in = null;
if (tags.extraHeaderInfoAvailable) {
in = yaffs_FindOrCreateObjectByNumber
(dev, tags.objectId,
tags.extraObjectType);
}
if (!(in != null) ||
// #ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD
// !in.valid ||
// #endif
tags.extraShadows ||
(!in.valid &&
(tags.objectId == Guts_H.YAFFS_OBJECTID_ROOT ||
tags.objectId == Guts_H.YAFFS_OBJECTID_LOSTNFOUND))
) {
/* 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.
*/
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(dev,
chunk,
chunkData, chunkDataIndex,
null);
oh = /*(yaffs_ObjectHeader *)*/ new yaffs_ObjectHeader(chunkData,
chunkDataIndex);
if (!(in != null))
in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh.type());
}
if (!(in != null)) {
/* TODO Hoosterman we have a problem! */
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("yaffs tragedy: Could not make object for object %d " +
"at chunk %d during scan"
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(tags.objectId), PrimitiveWrapperFactory.get(chunk));
}
yaffs_ScanBackward_Subminus1(dev);
}
}
if (state == Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
/* If we got this far while scanning, then the block is fully allocated. */
state = Guts_H.YAFFS_BLOCK_STATE_FULL;
}
bi.setBlockState(state);
/* Now let's see if it was dirty */
if (bi.pagesInUse() == 0 &&
!bi.hasShrinkHeader() &&
bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_FULL) {
yaffs_BlockBecameDirty(dev, blk);
}
}
return yaffs_ScanBackward_Sub1(dev);
}
static void yaffs_ScanBackward_Subminus1(yaffs_Device dev)
{
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 == Guts_H.YAFFS_OBJECT_TYPE_FILE) &&
((oh != null &&
oh.type() == Guts_H.YAFFS_OBJECT_TYPE_FILE)||
(tags.extraHeaderInfoAvailable &&
tags.extraObjectType == Guts_H.YAFFS_OBJECT_TYPE_FILE))
) {
/*__u32*/ int thisSize =
(oh != null) ? oh.fileSize() : tags.
extraFileLength;
/*__u32*/ int parentObjectId =
(oh != null) ? oh.
parentObjectId() : tags.
extraParentObjectId;
// PORT changed the name of the inner variable
/*unsigned*/ boolean isShrinkInner =
(oh != null) ? 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 ==
Guts_H.YAFFS_OBJECTID_DELETED
|| parentObjectId ==
Guts_H.YAFFS_OBJECTID_UNLINKED) {
thisSize = 0;
isShrinkInner = true;
}
if (isShrinkInner &&
Utils.intAsUnsignedInt(in.variant.fileVariant().
shrinkSize) > thisSize) {
in.variant.fileVariant().
shrinkSize =
thisSize;
}
if (isShrinkInner) {
bi.setHasShrinkHeader(true);
}
}
/* Use existing - destroy this one. */
yaffs_DeleteChunk(dev, chunk, true, 34 /*Utils.__LINE__()*/);
}
if (!in.valid &&
(tags.objectId == Guts_H.YAFFS_OBJECTID_ROOT ||
tags.objectId ==
Guts_H.YAFFS_OBJECTID_LOSTNFOUND)) {
/* We only load some info, don't fiddle with directory structure */
in.valid = true;
if(oh != null) {
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
} else {
in.variantType = tags.extraObjectType;
in.lazyLoaded = true;
}
in.chunkId = chunk;
} else if (!in.valid) {
/* we need to load this info */
in.valid = true;
in.chunkId = chunk;
if(oh != null) {
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
if (oh.shadowsObject() > 0)
yaffs_HandleShadowedObject(dev,
oh.
shadowsObject(),
true);
yaffs_SetObjectName(in, oh.name(), oh.nameIndex());
parent =
yaffs_FindOrCreateObjectByNumber
(dev, oh.parentObjectId(),
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY);
fileSize = oh.fileSize();
isShrink = oh.isShrink();
equivalentObjectId = oh.equivalentObjectId();
}
else {
in.variantType = tags.extraObjectType;
parent =
yaffs_FindOrCreateObjectByNumber
(dev, tags.extraParentObjectId,
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY);
fileSize = tags.extraFileLength;
isShrink = tags.extraIsShrinkHeader;
equivalentObjectId = tags.extraEquivalentObjectId;
in.lazyLoaded = true;
}
in.dirty = false;
/* directory stuff...
* hook up to parent
*/
if (parent.variantType ==
Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN) {
/* Set up as a directory */
parent.variantType =
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY;
devextras.INIT_LIST_HEAD(parent.variant.
directoryVariant().
children);
} else if (parent.variantType !=
Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY)
{
/* Hoosterman, another problem....
* We're trying to use a non-directory as a directory
*/
yportenv.T(yportenv.YAFFS_TRACE_ERROR,
("yaffs tragedy: attempting to use non-directory as" +
" a directory in scan. Put in lost+found."
+ ydirectenv.TENDSTR));
parent = dev.lostNFoundDir;
}
yaffs_AddObjectToDirectory(parent, in);
itsUnlinked = (parent == dev.deletedDir) ||
(parent == dev.unlinkedDir);
if (isShrink) {
/* Mark the block as having a shrinkHeader */
bi.setHasShrinkHeader(true);
}
/* 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 Guts_H.YAFFS_OBJECT_TYPE_UNKNOWN:
/* Todo got a problem */
break;
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
if (in.variant.fileVariant().
scannedFileSize < fileSize) {
/* This covers the case where the file size is greater
* than where the data is
* This will happen if the file is resized to be larger
* than its current data extents.
*/
in.variant.fileVariant().fileSize = fileSize;
in.variant.fileVariant().scannedFileSize =
in.variant.fileVariant().fileSize;
}
if (isShrink &&
Utils.intAsUnsignedInt(in.variant.fileVariant().shrinkSize) > fileSize) {
in.variant.fileVariant().shrinkSize = fileSize;
}
break;
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
if(!itsUnlinked) {
in.variant.hardLinkVariant().equivalentObjectId =
equivalentObjectId;
in.hardLinks.next =
/*(list_head)*/ hardList;
hardList = in;
}
break;
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
/* Do nothing */
break;
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
/* Do nothing */
break;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
if(oh != null)
{
in.variant.symLinkVariant().alias =
yaffs_CloneString(oh.
alias(), oh.aliasIndex());
in.variant.symLinkVariant().aliasIndex = 0;
}
break;
}
}
}
static boolean yaffs_ScanBackward_Sub1(yaffs_Device dev)
{
if (altBlockIndex)
ydirectenv.YFREE_ALT(blockIndex);
else
ydirectenv.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.
*/
yaffs_HardlinkFixup(dev,hardList);
/*
* Sort out state of unlinked and deleted objects.
*/
{
list_head i;
list_head n;
yaffs_Object l;
/* Soft delete all the unlinked files */
i = dev.unlinkedDir.variant.directoryVariant().children.next();
n = i.next();
// list_for_each_safe(i, n,
// dev.unlinkedDir.variant.directoryVariant().
// children) {
while (i != dev.unlinkedDir.variant.directoryVariant().children) {
if (i != null) {
l = /*list_entry(i, yaffs_Object, siblings)*/ (yaffs_Object)i.list_entry;
yaffs_DestroyObject(l);
}
i = n;
n = i.next();
}
/* Soft delete all the deletedDir files */
i = dev.deletedDir.variant.directoryVariant().children.next();
n = i.next();
// list_for_each_safe(i, n,
// dev.deletedDir.variant.directoryVariant().
// children) {
while (i != dev.deletedDir.variant.directoryVariant().children) {
if (i != null) {
l = /*list_entry(i, yaffs_Object, siblings)*/ (yaffs_Object)i.list_entry;
yaffs_DestroyObject(l);
}
i = n;
n = i.next();
}
}
yaffs_ReleaseTempBuffer(dev, chunkData, 35 /*Utils.__LINE__()*/);
yportenv.T(yportenv.YAFFS_TRACE_SCAN, (("yaffs_ScanBackwards ends" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_OK;
}
/*------------------------------ Directory Functions ----------------------------- */
static void yaffs_RemoveObjectFromDirectory(yaffs_Object obj)
{
yaffs_Device dev = obj.myDev;
if(dev != null && dev.subField1.removeObjectCallback != null)
dev.subField1.removeObjectCallback.yaffsfs_RemoveObjectCallback(obj);
devextras.list_del_init(obj.siblings);
obj.parent = null;
}
static void yaffs_AddObjectToDirectory(yaffs_Object directory,
yaffs_Object obj)
{
if (!(directory != null)) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("tragedy: Trying to add an object to a null pointer directory"
+ ydirectenv.TENDSTR));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
if (directory.variantType != Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("tragedy: Trying to add an object to a non-directory"
+ ydirectenv.TENDSTR));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
if (obj.siblings.prev == null) {
/* Not initialised */
devextras.INIT_LIST_HEAD(obj.siblings);
} else if (!devextras.list_empty(obj.siblings)) {
/* If it is holed up somewhere else, un hook it */
yaffs_RemoveObjectFromDirectory(obj);
}
/* Now add it */
devextras.list_add(obj.siblings, directory.variant.directoryVariant().children);
obj.parent = directory;
if (directory == obj.myDev.unlinkedDir
|| directory == obj.myDev.deletedDir) {
obj.sub.unlinked = true;
obj.myDev.nUnlinkedFiles++;
obj.renameAllowed = false;
}
}
static yaffs_Object yaffs_FindObjectByName(yaffs_Object directory,
/*const YCHAR **/ byte[] name, int nameIndex)
{
short sum;
list_head i;
/*YCHAR buffer[Guts_H.YAFFS_MAX_NAME_LENGTH + 1]*/ byte[] buffer = new byte[Guts_H.YAFFS_MAX_NAME_LENGTH + 1];
final int bufferIndex = 0;
yaffs_Object l;
if (!(name != null)) {
return null;
}
if (!(directory != null)) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(
("tragedy: yaffs_FindObjectByName: null pointer directory"
+ ydirectenv.TENDSTR)));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
if (directory.variantType != Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(
("tragedy: yaffs_FindObjectByName: non-directory" + ydirectenv.TENDSTR)));
yaffs2.utils.Globals.portConfiguration.YBUG();
}
sum = yaffs_CalcNameSum(name, nameIndex);
// list_for_each(i, &directory.variant.directoryVariant.children) {
for (i = directory.variant.directoryVariant().children.next();
i != directory.variant.directoryVariant().children; i = i.next()) {
if (i != null) {
l = /*list_entry(i, yaffs_Object, siblings)*/ (yaffs_Object)i.list_entry;
yaffs_CheckObjectDetailsLoaded(l);
/* Special case for lost-n-found */
if (l.objectId == Guts_H.YAFFS_OBJECTID_LOSTNFOUND) {
if (ydirectenv.yaffs_strcmp(name, nameIndex, ydirectenv.YAFFS_LOSTNFOUND_NAME, 0) == 0) {
return l;
}
} else if (ydirectenv.yaffs_SumCompare(l.sum, sum) || l.chunkId <= 0)
{
/* LostnFound cunk called Objxxx
* Do a real check
*/
yaffs_GetObjectName(l, buffer, bufferIndex,
Guts_H.YAFFS_MAX_NAME_LENGTH);
if (ydirectenv.yaffs_strcmp(name, nameIndex, buffer, bufferIndex) == 0) {
return l;
}
}
}
}
return null;
}
// #if 0
// int yaffs_ApplyToDirectoryChildren(yaffs_Object theDir,
// int (*fn) (yaffs_Object ))
// {
// struct list_head *i;
// yaffs_Object l;
// if (!theDir) {
// yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
// (TSTR
// ("tragedy: yaffs_FindObjectByName: null pointer directory"
// + ydirectenv.TENDSTR)));
// YBUG();
// }
// if (theDir.variantType != Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY) {
// yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
// (TSTR
// ("tragedy: yaffs_FindObjectByName: non-directory" + ydirectenv.TENDSTR)));
// YBUG();
// }
// list_for_each(i, &theDir.variant.directoryVariant.children) {
// if (i) {
// l = list_entry(i, yaffs_Object, siblings);
// if (l && !fn(l)) {
// return Guts_H.YAFFS_FAIL;
// }
// }
// }
// return Guts_H.YAFFS_OK;
// }
// #endif
/* GetEquivalentObject dereferences any hard links to get to the
* actual object.
*/
static yaffs_Object yaffs_GetEquivalentObject(yaffs_Object obj)
{
if (obj != null && obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_HARDLINK) {
/* We want the object id of the equivalent object, not this one */
obj = obj.variant.hardLinkVariant().equivalentObject;
}
return obj;
}
static int yaffs_GetObjectName(yaffs_Object obj, /*YCHAR **/ byte[] name,
int nameIndex,int buffSize)
{
Unix.memset(name, nameIndex, (byte)0, buffSize/* * sizeof(YCHAR)*/ );
yaffs_CheckObjectDetailsLoaded(obj);
if (obj.objectId == Guts_H.YAFFS_OBJECTID_LOSTNFOUND) {
ydirectenv.yaffs_strncpy(name, nameIndex, ydirectenv.YAFFS_LOSTNFOUND_NAME, 0, buffSize - 1);
} else if (obj.chunkId <= 0) {
/*YCHAR locName[20]*/ byte[] locName = new byte[20];
final int locNameIndex = 0;
/* make up a name */
Unix.xprintfArgs[0] = PrimitiveWrapperFactory.get(ydirectenv.YAFFS_LOSTNFOUND_PREFIX);
Unix.xprintfArgs[1] = PrimitiveWrapperFactory.get(0);
Unix.xprintfArgs[2] = PrimitiveWrapperFactory.get(obj.objectId);
Unix.sprintf(locName, locNameIndex, "%a%d");
ydirectenv.yaffs_strncpy(name, nameIndex, locName, locNameIndex, buffSize - 1);
}
// #ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
else if (obj.shortName[obj.shortNameIndex] != 0) {
ydirectenv.yaffs_strcpy(name, nameIndex, obj.shortName, obj.shortNameIndex);
}
// #endif
else {
boolean result;
/*__u8 **/ byte[] buffer = yaffs_GetTempBuffer(obj.myDev, 36 /*Utils.__LINE__()*/);
final int bufferIndex = 0;
yaffs_ObjectHeader oh = /*(yaffs_ObjectHeader) buffer*/
new yaffs_ObjectHeader(buffer, bufferIndex);
Unix.memset(buffer, bufferIndex, (byte)0, obj.myDev.subField1.nDataBytesPerChunk);
if (obj.chunkId >= 0) {
result = yaffs_nand_C.yaffs_ReadChunkWithTagsFromNAND(obj.myDev,
obj.chunkId, buffer, bufferIndex,
null);
}
ydirectenv.yaffs_strncpy(name, nameIndex, oh.name(), oh.nameIndex(), buffSize - 1);
yaffs_ReleaseTempBuffer(obj.myDev, buffer, 37 /*Utils.__LINE__()*/);
}
return ydirectenv.yaffs_strlen(name, nameIndex);
}
static int yaffs_GetObjectFileLength(yaffs_Object obj)
{
/* Dereference any hard linking */
obj = yaffs_GetEquivalentObject(obj);
if (obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_FILE) {
return obj.variant.fileVariant().fileSize;
}
if (obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_SYMLINK) {
return ydirectenv.yaffs_strlen(obj.variant.symLinkVariant().alias,
obj.variant.symLinkVariant().aliasIndex);
} else {
/* Only a directory should drop through to here */
return obj.myDev.subField1.nDataBytesPerChunk;
}
}
static int yaffs_GetObjectLinkCount(yaffs_Object obj)
{
int count = 0;
list_head i;
if (!obj.sub.unlinked) {
count++; /* the object itself */
}
// list_for_each(i, &obj.hardLinks) {
for (i = obj.hardLinks.next(); i != obj.hardLinks; i = i.next()) {
count++; /* add the hard links; */
}
return count;
}
static int yaffs_GetObjectInode(yaffs_Object obj)
{
obj = yaffs_GetEquivalentObject(obj);
return obj.objectId;
}
static /*unsigned*/ int yaffs_GetObjectType(yaffs_Object obj)
{
obj = yaffs_GetEquivalentObject(obj);
switch (obj.variantType) {
case Guts_H.YAFFS_OBJECT_TYPE_FILE:
return devextras.DT_REG;
// break;
case Guts_H.YAFFS_OBJECT_TYPE_DIRECTORY:
return devextras.DT_DIR;
// break;
case Guts_H.YAFFS_OBJECT_TYPE_SYMLINK:
return devextras.DT_LNK;
// break;
case Guts_H.YAFFS_OBJECT_TYPE_HARDLINK:
return devextras.DT_REG;
// break;
case Guts_H.YAFFS_OBJECT_TYPE_SPECIAL:
if (Unix.S_ISFIFO(obj.yst_mode))
return devextras.DT_FIFO;
if (Unix.S_ISCHR(obj.yst_mode))
return devextras.DT_CHR;
if (Unix.S_ISBLK(obj.yst_mode))
return devextras.DT_BLK;
if (Unix.S_ISSOCK(obj.yst_mode))
return devextras.DT_SOCK;
default:
return devextras.DT_REG;
// break;
}
}
static /*YCHAR **/ byte[] yaffs_GetSymlinkAlias(yaffs_Object obj)
{
obj = yaffs_GetEquivalentObject(obj);
if (obj.variantType == Guts_H.YAFFS_OBJECT_TYPE_SYMLINK) {
return yaffs_CloneString(obj.variant.symLinkVariant().alias,
obj.variant.symLinkVariant().aliasIndex);
} else {
return yaffs_CloneString(new byte[] {'\0'}, 0);
}
}
// #ifndef CONFIG_YAFFS_WINCE
static boolean yaffs_SetAttributes(yaffs_Object obj, iattr attr)
{
/*unsigned int*/ int valid = attr.ia_valid;
if ((valid & devextras.ATTR_MODE) != 0)
obj.yst_mode = attr.ia_mode;
if ((valid & devextras.ATTR_UID) != 0)
obj.yst_uid = attr.ia_uid;
if ((valid & devextras.ATTR_GID) != 0)
obj.yst_gid = attr.ia_gid;
if ((valid & devextras.ATTR_ATIME) != 0)
obj.yst_atime = /*Y_TIME_CONVERyportenv.T(*/ attr.ia_atime/*)*/;
if ((valid & devextras.ATTR_CTIME) != 0)
obj.yst_ctime = /*Y_TIME_CONVERyportenv.T(*/ attr.ia_ctime/*)*/;
if ((valid & devextras.ATTR_MTIME) != 0)
obj.yst_mtime = /*Y_TIME_CONVERyportenv.T(*/ attr.ia_mtime/*)*/;
if ((valid & devextras.ATTR_SIZE) != 0)
yaffs_ResizeFile(obj, attr.ia_size);
yaffs_UpdateObjectHeader(obj, null, 0, true, false, 0);
return Guts_H.YAFFS_OK;
}
static boolean yaffs_GetAttributes(yaffs_Object obj, iattr attr)
{
/*unsigned int*/ int valid = 0;
attr.ia_mode = obj.yst_mode;
valid |= devextras.ATTR_MODE;
attr.ia_uid = obj.yst_uid;
valid |= devextras.ATTR_UID;
attr.ia_gid = obj.yst_gid;
valid |= devextras.ATTR_GID;
/*Y_TIME_CONVERyportenv.T(*/ attr.ia_atime/*)*/ = obj.yst_atime;
valid |= devextras.ATTR_ATIME;
/*Y_TIME_CONVERyportenv.T(*/ attr.ia_ctime/*)*/ = obj.yst_ctime;
valid |= devextras.ATTR_CTIME;
/*Y_TIME_CONVERyportenv.T(*/ attr.ia_mtime/*)*/ = obj.yst_mtime;
valid |= devextras.ATTR_MTIME;
attr.ia_size = yaffs_GetFileSize(obj);
valid |= devextras.ATTR_SIZE;
attr.ia_valid = valid;
return Guts_H.YAFFS_OK;
}
// #endif
// #if 0
// int yaffs_DumpObject(yaffs_Object obj)
// {
// YCHAR name[257];
// yaffs_GetObjectName(obj, name, 256);
// yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
// (TSTR
// ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d"
// " chunk %d type %d size %d\n"
// + ydirectenv.TENDSTR), obj.objectId, yaffs_GetObjectInode(obj), name,
// obj.dirty, obj.valid, obj.serial, obj.sum, obj.chunkId,
// yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
// return Guts_H.YAFFS_OK;
// }
// #endif
/*---------------------------- Initialisation code -------------------------------------- */
static boolean yaffs_CheckDevFunctions(yaffs_Device dev)
{
/* Common functions, gotta have */
if (!(dev.subField1.eraseBlockInNAND != null) || !(dev.subField1.initialiseNAND != null))
return false;
// #ifdef CONFIG_YAFFS_YAFFS2
/* Can use the "with tags" style interface for yaffs1 or yaffs2 */
if (dev.subField1.writeChunkWithTagsToNAND != null &&
dev.subField1.readChunkWithTagsFromNAND != null &&
!(dev.subField1.writeChunkToNAND != null) &&
!(dev.subField1.readChunkFromNAND != null) &&
dev.subField1.markNANDBlockBad != null && dev.subField1.queryNANDBlock != null)
return true;
// #endif
/* Can use the "spare" style interface for yaffs1 */
if (!dev.subField1.isYaffs2 &&
!(dev.subField1.writeChunkWithTagsToNAND != null) &&
!(dev.subField1.readChunkWithTagsFromNAND != null) &&
dev.subField1.writeChunkToNAND != null &&
dev.subField1.readChunkFromNAND != null &&
!(dev.subField1.markNANDBlockBad != null) && !(dev.subField1.queryNANDBlock != null))
return true;
return false; /* bad */
}
static void yaffs_CreateInitialDirectories(yaffs_Device dev)
{
/* Initialise the unlinked, deleted, root and lost and found directories */
dev.lostNFoundDir = dev.rootDir = null;
dev.unlinkedDir = dev.deletedDir = null;
dev.unlinkedDir =
yaffs_CreateFakeDirectory(dev, Guts_H.YAFFS_OBJECTID_UNLINKED, Unix.S_IFDIR);
dev.deletedDir =
yaffs_CreateFakeDirectory(dev, Guts_H.YAFFS_OBJECTID_DELETED, Unix.S_IFDIR);
dev.rootDir =
yaffs_CreateFakeDirectory(dev, Guts_H.YAFFS_OBJECTID_ROOT,
ydirectenv.YAFFS_ROOT_MODE | Unix.S_IFDIR);
dev.lostNFoundDir =
yaffs_CreateFakeDirectory(dev, Guts_H.YAFFS_OBJECTID_LOSTNFOUND,
ydirectenv.YAFFS_LOSTNFOUND_MODE | Unix.S_IFDIR);
yaffs_AddObjectToDirectory(dev.rootDir, dev.lostNFoundDir);
}
// XXX mark staticialized variables
/*unsigned*/ static int x;
static int bits;
static boolean yaffs_GutsInitialise(yaffs_Device dev)
{
yportenv.T(yportenv.YAFFS_TRACE_TRACING, (("yaffs: yaffs_GutsInitialise()" + ydirectenv.TENDSTR)));
/* Check stuff that must be set */
if (!(dev != null)) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS, (("yaffs: Need a device" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
dev.subField2.internalStartBlock = dev.subField1.startBlock;
dev.subField2.internalEndBlock = dev.subField1.endBlock;
dev.subField2.blockOffset = 0;
dev.subField2.chunkOffset = 0;
dev.subField3.nFreeChunks = 0;
if (dev.subField1.startBlock == 0) {
dev.subField2.internalStartBlock = dev.subField1.startBlock + 1;
dev.subField2.internalEndBlock = dev.subField1.endBlock + 1;
dev.subField2.blockOffset = 1;
dev.subField2.chunkOffset = dev.subField1.nChunksPerBlock;
}
/* Check geometry parameters. */
if ((dev.subField1.isYaffs2 && dev.subField1.nDataBytesPerChunk < 1024) ||
(!dev.subField1.isYaffs2 && dev.subField1.nDataBytesPerChunk != 512) ||
dev.subField1.nChunksPerBlock < 2 ||
dev.subField1.nReservedBlocks < 2 ||
dev.subField2.internalStartBlock <= 0 ||
dev.subField2.internalEndBlock <= 0 ||
dev.subField2.internalEndBlock <= (dev.subField2.internalStartBlock + dev.subField1.nReservedBlocks + 2) // otherwise it is too small
) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s "
+ ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField1.nDataBytesPerChunk), PrimitiveWrapperFactory.get(dev.subField1.isYaffs2 ? "2" : ""));
return Guts_H.YAFFS_FAIL;
}
if (yaffs_nand_C.yaffs_InitialiseNAND(dev) != Guts_H.YAFFS_OK) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(("yaffs: InitialiseNAND failed" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
/* Got the right mix of functions? */
if (!yaffs_CheckDevFunctions(dev)) {
/* Function missing */
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(
("yaffs: device function(s) missing or wrong\n" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
/* This is really a compilation check. */
if (!yaffs_CheckStructures()) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(("yaffs_CheckStructures failed\n" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
if (dev.subField2.isMounted) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(("yaffs: device already mounted\n" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
/* Finished with most checks. One or two more checks happen later on too. */
dev.subField2.isMounted = true;
/* OK now calculate a few things for the device */
/*
* Calculate all the chunk size manipulation numbers:
*/
/* Start off assuming it is a power of 2 */
dev.subField2.chunkShift = ShiftDiv(dev.subField1.nDataBytesPerChunk);
dev.subField2.chunkMask = (1<<dev.subField2.chunkShift) - 1;
if(dev.subField1.nDataBytesPerChunk == (dev.subField2.chunkMask + 1)){
/* Yes it is a power of 2, disable crumbs */
dev.subField2.crumbMask = 0;
dev.subField2.crumbShift = 0;
dev.subField2.crumbsPerChunk = 0;
} else {
/* Not a power of 2, use crumbs instead */
dev.subField2.crumbShift = ShiftDiv(yaffs_PackedTags2TagsPart.SERIALIZED_LENGTH);
dev.subField2.crumbMask = (1<<dev.subField2.crumbShift)-1;
dev.subField2.crumbsPerChunk = dev.subField1.nDataBytesPerChunk/(1 << dev.subField2.crumbShift);
dev.subField2.chunkShift = 0;
dev.subField2.chunkMask = 0;
}
return yaffs_GutsInitialise_Sub0(dev);
}
protected static boolean yaffs_GutsInitialise_Sub0(yaffs_Device dev)
{
/*
* Calculate chunkGroupBits.
* We need to find the next power of 2 > than internalEndBlock
*/
x = dev.subField1.nChunksPerBlock * (dev.subField2.internalEndBlock + 1); // XXX understand, I'm too tired now
bits = ShiftsGE(x);
/* Set up tnode width if wide tnodes are enabled. */
if(!dev.subField1.wideTnodesDisabled){
/* bits must be even so that we end up with 32-bit words */
if((bits & 1) != 0)
bits++;
if(bits < 16)
dev.subField2.tnodeWidth = 16;
else
dev.subField2.tnodeWidth = bits;
}
else
dev.subField2.tnodeWidth = 16;
dev.subField2.tnodeMask = (1<<dev.subField2.tnodeWidth)-1;
/* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled),
* so if the bitwidth of the
* chunk range we're using is greater than 16 we need
* to figure out chunk shift and chunkGroupSize
*/
if (bits <= dev.subField2.tnodeWidth)
dev.subField1.chunkGroupBits = 0;
else
dev.subField1.chunkGroupBits = bits - dev.subField2.tnodeWidth;
dev.subField1.chunkGroupSize = 1 << dev.subField1.chunkGroupBits;
if (dev.subField1.nChunksPerBlock < dev.subField1.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".
*/
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(("yaffs: chunk group too large\n" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_FAIL;
}
/* OK, we've finished verifying the device, lets continue with initialisation */
/* More device initialisation */
dev.subField3.garbageCollections = 0;
dev.subField3.passiveGarbageCollections = 0;
dev.subField3.currentDirtyChecker = 0;
dev.bufferedBlock = -1;
dev.doingBufferedBlockRewrite = 0;
dev.nDeletedFiles = 0;
dev.nBackgroundDeletions = 0;
dev.nUnlinkedFiles = 0;
dev.subField3.eccFixed = 0;
dev.subField3.eccUnfixed = 0;
dev.subField3.tagsEccFixed = 0;
dev.tagsEccUnfixed = 0;
dev.subField3.nErasureFailures = 0;
dev.subField3.nErasedBlocks = 0;
dev.subField3.isDoingGC = false;
dev.hasPendingPrioritisedGCs = true; /* Assume the worst for now, will get fixed on first GC */
/* Initialise temporary buffers and caches. */
{
int i;
for (i = 0; i < Guts_H.YAFFS_N_TEMP_BUFFERS; i++) {
dev.tempBuffer[i].line = 0; /* not in use */
dev.tempBuffer[i].buffer =
ydirectenv.YMALLOC_DMA(dev.subField1.nDataBytesPerChunk);
}
}
if (dev.subField1.nShortOpCaches > 0) {
int i;
if (dev.subField1.nShortOpCaches > Guts_H.YAFFS_MAX_SHORT_OP_CACHES) {
dev.subField1.nShortOpCaches = Guts_H.YAFFS_MAX_SHORT_OP_CACHES;
}
dev.srCache =
ydirectenv.YMALLOC_CHUNKCACHE(dev.subField1.nShortOpCaches/* * sizeof(yaffs_ChunkCache)*/);
for (i = 0; i < dev.subField1.nShortOpCaches; i++) {
dev.srCache[i].object = null;
dev.srCache[i].lastUse = 0;
dev.srCache[i].dirty = false;
dev.srCache[i].data = ydirectenv.YMALLOC_DMA(dev.subField1.nDataBytesPerChunk);
dev.srCache[i].dataIndex = 0;
}
dev.srLastUse = 0;
}
dev.cacheHits = 0;
dev.subField3.gcCleanupList = ydirectenv.YMALLOC_INT(dev.subField1.nChunksPerBlock /*sizeof(__u32)*/);
if (dev.subField1.isYaffs2) {
dev.subField1.useHeaderFileSize = true;
}
yaffs_InitialiseBlocks(dev);
yaffs_InitialiseTnodes(dev);
yaffs_InitialiseObjects(dev);
yaffs_CreateInitialDirectories(dev);
/* Now scan the flash. */
if (dev.subField1.isYaffs2) {
if(yaffs_CheckpointRestore(dev)) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
(("yaffs: restored from checkpoint" + ydirectenv.TENDSTR)));
} else {
/* Clean up the mess caused by an aborted checkpoint load
* and scan backwards.
*/
yaffs_DeinitialiseBlocks(dev);
yaffs_DeinitialiseTnodes(dev);
yaffs_DeinitialiseObjects(dev);
yaffs_InitialiseBlocks(dev);
yaffs_InitialiseTnodes(dev);
yaffs_InitialiseObjects(dev);
yaffs_CreateInitialDirectories(dev);
yaffs_ScanBackwards(dev);
}
}else
yaffs_Scan(dev);
/* Zero out stats */
dev.subField3.nPageReads = 0;
dev.subField3.nPageWrites = 0;
dev.subField3.nBlockErasures = 0;
dev.subField3.nGCCopies = 0;
dev.subField3.nRetriedWrites = 0;
dev.subField3.nRetiredBlocks = 0;
yaffs_VerifyFreeChunks(dev);
yportenv.T(yportenv.YAFFS_TRACE_TRACING,
(("yaffs: yaffs_GutsInitialise() done.\n" + ydirectenv.TENDSTR)));
return Guts_H.YAFFS_OK;
}
static void yaffs_Deinitialise(yaffs_Device dev)
{
if (dev.subField2.isMounted) {
int i;
yaffs_DeinitialiseBlocks(dev);
yaffs_DeinitialiseTnodes(dev);
yaffs_DeinitialiseObjects(dev);
if (dev.subField1.nShortOpCaches > 0) {
for (i = 0; i < dev.subField1.nShortOpCaches; i++) {
ydirectenv.YFREE(dev.srCache[i].data);
}
ydirectenv.YFREE(dev.srCache);
}
ydirectenv.YFREE(dev.subField3.gcCleanupList);
for (i = 0; i < Guts_H.YAFFS_N_TEMP_BUFFERS; i++) {
ydirectenv.YFREE(dev.tempBuffer[i].buffer);
}
dev.subField2.isMounted = false;
}
}
static int yaffs_CountFreeChunks(yaffs_Device dev)
{
int nFree;
int b;
yaffs_BlockInfo blk;
for (nFree = 0, b = dev.subField2.internalStartBlock; b <= dev.subField2.internalEndBlock;
b++) {
blk = Guts_H.yaffs_GetBlockInfo(dev, b);
switch (blk.blockState()) {
case Guts_H.YAFFS_BLOCK_STATE_EMPTY:
case Guts_H.YAFFS_BLOCK_STATE_ALLOCATING:
case Guts_H.YAFFS_BLOCK_STATE_COLLECTING:
case Guts_H.YAFFS_BLOCK_STATE_FULL:
nFree +=
(dev.subField1.nChunksPerBlock - blk.pagesInUse() +
blk.softDeletions());
break;
default:
break;
}
}
return nFree;
}
static int yaffs_GetNumberOfFreeChunks(yaffs_Device dev)
{
/* This is what we report to the outside world */
int nFree;
int nDirtyCacheChunks;
int blocksForCheckpoint;
// #if 1
nFree = dev.subField3.nFreeChunks;
// #else
// nFree = yaffs_CountFreeChunks(dev);
// #endif
nFree += dev.nDeletedFiles;
/* Now count the number of dirty chunks in the cache and subtract those */
{
int i;
for (nDirtyCacheChunks = 0, i = 0; i < dev.subField1.nShortOpCaches; i++) {
if (dev.srCache[i].dirty)
nDirtyCacheChunks++;
}
}
nFree -= nDirtyCacheChunks;
nFree -= ((dev.subField1.nReservedBlocks + 1) * dev.subField1.nChunksPerBlock);
/* Now we figure out how much to reserve for the checkpoint and report that... */
blocksForCheckpoint = dev.subField1.nCheckpointReservedBlocks - dev.subField2.blocksInCheckpoint;
if(blocksForCheckpoint < 0)
blocksForCheckpoint = 0;
nFree -= (blocksForCheckpoint * dev.subField1.nChunksPerBlock);
if (nFree < 0)
nFree = 0;
return nFree;
}
static int yaffs_freeVerificationFailures;
static void yaffs_VerifyFreeChunks(yaffs_Device dev)
{
int counted = yaffs_CountFreeChunks(dev);
int difference = dev.subField3.nFreeChunks - counted;
if (difference != 0) {
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,
("Freechunks verification failure %d %d %d" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(dev.subField3.nFreeChunks), PrimitiveWrapperFactory.get(counted), PrimitiveWrapperFactory.get(difference));
yaffs_freeVerificationFailures++;
}
}
/*---------------------------------------- YAFFS test code ----------------------*/
// #define yaffs_CheckStruct(structure,syze, name) \
// if(sizeof(structure) != syze) \
// { \
// yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,(("%s should be %d but is %d\n" + ydirectenv.TENDSTR),\
// name,syze,sizeof(structure))); \
// return Guts_H.YAFFS_FAIL; \
// }
static boolean yaffs_CheckStruct(int structureSize, int syze, String name)
{
if(/*sizeof(structure)*/ structureSize != syze)
{
yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,("%s should be %d but is %d\n" + ydirectenv.TENDSTR),
PrimitiveWrapperFactory.get(name),PrimitiveWrapperFactory.get(syze),/*sizeof(structure)*/ PrimitiveWrapperFactory.get(structureSize));
return false;
}
return true;
}
static boolean yaffs_CheckStructures()
{
return
/* yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") */
/* yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") */
/* yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") */
// #ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
(yaffs_CheckStruct(yaffs_Tnode.SERIALIZED_LENGTH, 2 * Guts_H.YAFFS_NTNODES_LEVEL0, "yaffs_Tnode") &&
// #endif
yaffs_CheckStruct(yaffs_ObjectHeader.SERIALIZED_LENGTH, 512, "yaffs_ObjectHeader"));
}
}