// 1 - Check files sizes
//========================
// -- Allocation table
long atFileSize = allocationTableRandomAccessFile.length();
if (atFileSize < AT_HEADER_SIZE+AT_BLOCK_SIZE) /* Should have at least one entry */
throw new DataStoreException("Allocation table is truncated : "+allocationTableFile.getAbsolutePath());
// Read some header fields
FileInputStream inFile = new FileInputStream(allocationTableFile);
DataInputStream in = new DataInputStream(new BufferedInputStream(inFile,16384));
int blockCount = in.readInt();
int blockSize = in.readInt();
int firstBlock = in.readInt();
// Fix AT size
long expectedATFileSize = AT_HEADER_SIZE+AT_BLOCK_SIZE*(long)blockCount;
if (atFileSize != expectedATFileSize)
{
log.error("Allocation table has an invalid size (actual:"+atFileSize+",expected:"+expectedATFileSize+"), fixing.");
allocationTableRandomAccessFile.setLength(expectedATFileSize);
}
// Fix data size
long dataFileSize = dataRandomAccessFile.length();
long expectedDataFileSize = (long)blockSize*blockCount;
if (dataFileSize != expectedDataFileSize)
{
log.error("Data file has an invalid size (actual:"+dataFileSize+",expected:"+expectedDataFileSize+"), fixing.");
dataRandomAccessFile.setLength(expectedDataFileSize);
}
//============================
// 2 - Check allocation table
//============================
// Read the AT into memory
byte[] flags = new byte[blockCount];
int[] allocatedSize = new int[blockCount];
int[] previousBlock = new int[blockCount];
int[] nextBlock = new int[blockCount];
int blocksInUse = 0;
int msgCount = 0;
for (int n = 0 ; n < blockCount ; n++)
{
flags[n] = in.readByte();
allocatedSize[n] = in.readInt();
previousBlock[n] = in.readInt();
nextBlock[n] = in.readInt();
if (allocatedSize[n] != -1)
{
blocksInUse++;
if ((flags[n] & FLAG_START_BLOCK) > 0)
msgCount++;
}
}
in.close();
log.debug("Blocks in use before fix : "+blocksInUse);
log.debug("Messages count before fix : "+msgCount);
// Fix first block index
boolean changed = false;
if (firstBlock < -1 || firstBlock >= blockCount)
{
log.error("Invalid allocation table first block index ("+firstBlock+"), guessing new one ...");
firstBlock = guessFirstBlockIndex(blockCount, allocatedSize, nextBlock);
log.debug("Guessed first block index : "+firstBlock);
changed = true;
}
// Recover table
if (msgCount == 0)
{
if (firstBlock == -1)
{
// Table is empty, cleanup dirty entries
changed = changed || cleanupEmptyBlocks(blockCount, flags, allocatedSize, previousBlock, nextBlock);
}
else
{
log.error("First block index should be -1, clearing ...");
firstBlock = -1;
changed = true;
}
}
else
{
if (firstBlock == -1)
{
log.error("Invalid first block index, guessing value ...");
firstBlock = guessFirstBlockIndex(blockCount, allocatedSize, nextBlock);
log.debug("Guessed first block index : "+firstBlock);
changed = true;
}
changed = changed || fixBlocks(blockCount, blockSize, firstBlock, flags, allocatedSize, previousBlock, nextBlock);
changed = changed || cleanupEmptyBlocks(blockCount, flags, allocatedSize, previousBlock, nextBlock);
}
// Update the allocation file table
if (changed)
{
// Re-compute size
msgCount = 0;
blocksInUse = 0;
for (int n = 0 ; n < blockCount ; n++)
{
if (allocatedSize[n] != -1)
{
blocksInUse++;
if ((flags[n] & FLAG_START_BLOCK) > 0)
msgCount++;
}
}
log.debug("Blocks in use after fix : "+blocksInUse);
log.debug("Messages count after fix : "+msgCount);
log.debug("Allocation table was altered, saving ...");
allocationTableRandomAccessFile.seek(AT_HEADER_FIRSTBLOCK_OFFSET);
allocationTableRandomAccessFile.writeInt(firstBlock);
for (int n = 0 ; n < blockCount ; n++)
{
byte[] allocationBlock = new byte[AT_BLOCK_SIZE];
// Regroup I/O to improve performance
allocationBlock[AB_FLAGS_OFFSET] = flags[n];
allocationBlock[AB_ALLOCSIZE_OFFSET] = (byte)((allocatedSize[n] >>> 24) & 0xFF);
allocationBlock[AB_ALLOCSIZE_OFFSET+1] = (byte)((allocatedSize[n] >>> 16) & 0xFF);
allocationBlock[AB_ALLOCSIZE_OFFSET+2] = (byte)((allocatedSize[n] >>> 8) & 0xFF);
allocationBlock[AB_ALLOCSIZE_OFFSET+3] = (byte)((allocatedSize[n] >>> 0) & 0xFF);
allocationBlock[AB_PREVBLOCK_OFFSET] = (byte)((previousBlock[n] >>> 24) & 0xFF);
allocationBlock[AB_PREVBLOCK_OFFSET+1] = (byte)((previousBlock[n] >>> 16) & 0xFF);
allocationBlock[AB_PREVBLOCK_OFFSET+2] = (byte)((previousBlock[n] >>> 8) & 0xFF);
allocationBlock[AB_PREVBLOCK_OFFSET+3] = (byte)((previousBlock[n] >>> 0) & 0xFF);
allocationBlock[AB_NEXTBLOCK_OFFSET] = (byte)((nextBlock[n] >>> 24) & 0xFF);
allocationBlock[AB_NEXTBLOCK_OFFSET+1] = (byte)((nextBlock[n] >>> 16) & 0xFF);
allocationBlock[AB_NEXTBLOCK_OFFSET+2] = (byte)((nextBlock[n] >>> 8) & 0xFF);
allocationBlock[AB_NEXTBLOCK_OFFSET+3] = (byte)((nextBlock[n] >>> 0) & 0xFF);
allocationTableRandomAccessFile.seek(AT_HEADER_SIZE+n*AT_BLOCK_SIZE);
allocationTableRandomAccessFile.write(allocationBlock);
}
allocationTableRandomAccessFile.getFD().sync();
}
else
log.debug("Allocation table was not altered");
}
catch (IOException e)
{
throw new DataStoreException("Cannot check/fix store integrity : "+e);
}
}