com.caucho.db.blob.Inode Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.db.blob;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.caucho.db.block.Block;
import com.caucho.db.block.BlockStore;
import com.caucho.db.xa.RawTransaction;
import com.caucho.db.xa.StoreTransaction;
import com.caucho.util.Hex;
import com.caucho.util.L10N;
import com.caucho.vfs.OutputStreamWithBuffer;
import com.caucho.vfs.TempCharBuffer;
/**
* Represents the indexes for a BLOB or CLOB.
*
* The inode contains 16 long values
*
* 0) length of the saved file
* 1-14) direct fragment addresses (to 224k)
* 15) pointer to the indirect block
*
*
* Inline storage (120)
*
* If the length of the blob is less than 120, the blob is stored directly
* in the inode.
*
* mini fragment storage (3840)
*
* If the length of the blob is less than 3840, the blob is stored
* in mini-fragments of size 256 pointed by the inode's addresses.
*
* The maximum wasted space for mini-fragment storage is 255 bytes.
*
* direct block storage
*
* The first 14 block pointers (112k)
*
* indirect storage
*
* The indirect block (a 16k block) itself is divided into sections:
*
* 0-1023) single indirect fragment addresses (16M, 2^24)
* 1024-1535) double indirect block addresses (16G, 2^34)
* 1536-2047) triple indirect block addresses (to 8T, 2^43)
*
*/
public class Inode
{
private static final L10N L = new L10N(Inode.class);
private static final Logger log
= Logger.getLogger(Inode.class.getName());
public static final int INODE_SIZE = 128;
public static final int INLINE_BLOB_SIZE = INODE_SIZE - 8;
public static final int BLOCK_SIZE = BlockStore.BLOCK_SIZE;
public static final int MINI_FRAG_SIZE = BlockStore.MINI_FRAG_SIZE;
public static final int MINI_FRAG_BLOB_SIZE
= (INLINE_BLOB_SIZE / 8) * MINI_FRAG_SIZE;
public static final int INDIRECT_BLOCKS = BLOCK_SIZE / 8;
// direct addresses are stored in the inode itself (112k of data).
public static final int DIRECT_BLOCKS = 14;
// single indirect addresses are stored in the indirect block (4M data)
public static final int SINGLE_INDIRECT_BLOCKS = INDIRECT_BLOCKS / 2;
// double indirect addresses (2^31 = 2G data)
public static final int DOUBLE_INDIRECT_BLOCKS = INDIRECT_BLOCKS / 4;
// triple indirect addresses (2^41 = 2T data)
public static final int TRIPLE_INDIRECT_BLOCKS = INDIRECT_BLOCKS / 4;
// size cutoffs for various inline modes
public static final long INLINE_MAX = INLINE_BLOB_SIZE;
public static final int MINI_FRAG_MAX
= (INLINE_BLOB_SIZE / 8) * MINI_FRAG_SIZE;
public static final long DIRECT_MAX
= BLOCK_SIZE * DIRECT_BLOCKS;
public static final long SINGLE_INDIRECT_MAX
= DIRECT_MAX + SINGLE_INDIRECT_BLOCKS * BLOCK_SIZE;
public static final long DOUBLE_INDIRECT_MAX
= (SINGLE_INDIRECT_MAX
+ DOUBLE_INDIRECT_BLOCKS * (BLOCK_SIZE / 8L) * BLOCK_SIZE);
private BlockStore _store;
private final byte []_bytes = new byte[INODE_SIZE];
public Inode()
{
}
public Inode(BlockStore store, StoreTransaction xa)
{
_store = store;
}
public Inode(BlockStore store)
{
this(store, RawTransaction.create());
}
/**
* Returns the backing store.
*/
public BlockStore getStore()
{
return _store;
}
/**
* Returns the buffer.
*/
public byte []getBuffer()
{
return _bytes;
}
/**
* Returns the length.
*/
public long getLength()
{
return readLong(_bytes, 0);
}
public void init(BlockStore store, StoreTransaction xa,
byte []buffer, int offset)
{
_store = store;
System.arraycopy(buffer, offset, _bytes, 0, _bytes.length);
}
/**
* Reads into a buffer.
*
* @param inode the inode buffer
* @param inodeOffset the offset of the inode data in the buffer
* @param store the owning store
* @param fileOffset the offset into the file to read
* @param buffer the buffer receiving the data
* @param bufferOffset the offset into the receiving buffer
* @param bufferLength the maximum number of bytes to read
*
* @return the number of bytes read
*/
static void validate(BlockStore store, byte []inode, int inodeOffset)
{
long fileLength = readLong(inode, inodeOffset);
if (fileLength <= 0) {
return;
}
if (fileLength <= INLINE_MAX) {
return;
}
else if (fileLength <= MINI_FRAG_MAX) {
int fragMax = (int) (fileLength / MINI_FRAG_SIZE);
for (int i = 0; i < fragMax; i++) {
long fragAddr = readLong(inode, inodeOffset + (i + 1) * 8);
if (! store.isMiniFragBlock(fragAddr)) {
throw new IllegalStateException(L.l("expected mini fragment at 0x{0}, type={1}",
Long.toHexString(fragAddr),
store.getAllocationByAddress(fragAddr)));
}
return;
}
}
else {
int blockMax = (int) ((fileLength - 1) / BLOCK_SIZE);
int directMax = Math.min(blockMax, DIRECT_BLOCKS);
for (int i = 0; i < directMax; i++) {
long addr = readLong(inode, inodeOffset + (i + 1) * 8);
if (! store.isDataBlock(addr)) {
throw new IllegalStateException(L.l("expected data block at 0x{0}, type={1}",
Long.toHexString(addr),
store.getAllocationByAddress(addr)));
}
}
if (DIRECT_BLOCKS <= blockMax) {
long addr = readLong(inode, inodeOffset + (DIRECT_BLOCKS + 1) * 8);
if (! store.isInodePtrBlock(addr)) {
throw new IllegalStateException(L.l("expected indirect ptr block at 0x{0}, type={1}",
Long.toHexString(addr),
store.getAllocationByAddress(addr)));
}
}
}
}
/**
* Opens a read stream to the inode.
*/
public InputStream openInputStream()
{
return new BlobInputStream(this);
}
/**
* Writes the inode value to a stream.
*/
public void writeToStream(OutputStreamWithBuffer os)
throws IOException
{
writeToStream(os, 0, Long.MAX_VALUE / 2);
}
/**
* Writes the inode value to a stream.
*/
private void writeToStreamOld(OutputStreamWithBuffer os,
long offset, long length)
throws IOException
{
byte []buffer = os.getBuffer();
int writeLength = buffer.length;
int writeOffset = os.getBufferOffset();
while (length > 0) {
int sublen = writeLength - writeOffset;
if (sublen == 0) {
buffer = os.nextBuffer(writeOffset);
writeOffset = os.getBufferOffset();
sublen = writeLength - writeOffset;
}
if (length < sublen)
sublen = (int) length;
int len = read(_bytes, 0, _store,
offset,
buffer, writeOffset, sublen);
if (len <= 0)
break;
writeOffset += len;
offset += len;
length -= len;
}
os.setBufferOffset(writeOffset);
}
/**
* Writes the inode value to a stream.
*/
public void writeToStream(OutputStream os,
long offset, long length)
throws IOException
{
while (length > 0) {
int sublen = (int) length;
int len = readToOutput(_bytes, 0, _store,
offset, sublen, os);
if (len <= 0)
break;
offset += len;
length -= len;
}
}
/**
* Writes the inode value to a stream.
*/
public static int writeToStream(byte []inode, int inodeOffset,
BlockStore store,
OutputStream os,
long offset, long length)
throws IOException
{
int readLength = 0;
while (length > 0) {
int sublen = (int) length;
int len = readToOutput(inode, inodeOffset, store,
offset, sublen, os);
if (len <= 0)
return readLength;
offset += len;
length -= len;
readLength += len;
}
return readLength;
}
/**
* Writes the inode value to a stream.
*/
public void writeToWriter(Writer writer)
throws IOException
{
TempCharBuffer tempBuffer = TempCharBuffer.allocate();
char []buffer = tempBuffer.getBuffer();
int writeLength = buffer.length;
long offset = 0;
while (true) {
int sublen = writeLength;
int len = read(_bytes, 0, _store,
offset,
buffer, 0, sublen);
if (len <= 0)
break;
writer.write(buffer, 0, len);
offset += 2 * len;
}
TempCharBuffer.free(tempBuffer);
}
/**
* Reads into a buffer.
*
* @param inode the inode buffer
* @param inodeOffset the offset of the inode data in the buffer
* @param store the owning store
* @param fileOffset the offset into the file to read
* @param buffer the buffer receiving the data
* @param bufferOffset the offset into the receiving buffer
* @param bufferLength the maximum number of bytes to read
*
* @return the number of bytes read
*/
static int read(byte []inode, int inodeOffset,
BlockStore store,
long fileOffset,
byte []buffer, int bufferOffset, int bufferLength)
throws IOException
{
long fileLength = readLong(inode, inodeOffset);
int sublen = Math.min(bufferLength, (int) (fileLength - fileOffset));
if (sublen <= 0) {
return -1;
}
if (fileLength <= INLINE_MAX) {
System.arraycopy(inode, inodeOffset + 8 + (int) fileOffset,
buffer, bufferOffset, sublen);
return sublen;
}
else if (fileLength <= MINI_FRAG_MAX) {
long fragAddr = readMiniFragAddr(inode, inodeOffset, store, fileOffset);
int fragOffset = (int) (fileOffset % MINI_FRAG_SIZE);
sublen = Math.min(MINI_FRAG_SIZE - fragOffset, sublen);
store.readMiniFragment(fragAddr, fragOffset,
buffer, bufferOffset, sublen);
return sublen;
}
InodeUpdate update = new InodeUpdate(store, inode, inodeOffset);
try {
long addr = readBlockAddr(inode, inodeOffset, update, fileOffset);
int offset = (int) (fileOffset % BLOCK_SIZE);
sublen = Math.min(BLOCK_SIZE - offset, sublen);
store.readBlock(addr, offset, buffer, bufferOffset, sublen);
return sublen;
} catch (IllegalArgumentException e) {
e = new IllegalArgumentException(L.l("{0}\n inodeOffset={1} fileOffset=0x{2}\n {3}",
e.getMessage(),
inodeOffset,
Long.toHexString(fileOffset),
Hex.toHex(inode, inodeOffset, inode.length - inodeOffset)),
e);
e.fillInStackTrace();
e.printStackTrace();
throw e;
} finally {
update.close();
}
}
/**
* Reads into an output stream.
*
* @param inode the inode buffer
* @param inodeOffset the offset of the inode data in the buffer
* @param store the owning store
* @param fileOffset the offset into the file to read
* @param buffer the buffer receiving the data
* @param bufferOffset the offset into the receiving buffer
* @param bufferLength the maximum number of bytes to read
*
* @return the number of bytes read
*/
static int readToOutput(byte []inode, int inodeOffset,
BlockStore store,
long fileOffset,
int sublen,
OutputStream os)
throws IOException
{
long fileLength = readLong(inode, inodeOffset);
sublen = Math.min(sublen, (int) (fileLength - fileOffset));
if (sublen <= 0) {
return -1;
}
if (fileLength <= INLINE_MAX) {
os.write(inode, inodeOffset + 8 + (int) fileOffset, sublen);
return sublen;
}
else if (fileLength <= MINI_FRAG_MAX) {
long fragAddr = readMiniFragAddr(inode, inodeOffset, store, fileOffset);
int fragOffset = (int) (fileOffset % MINI_FRAG_SIZE);
sublen = Math.min(sublen, MINI_FRAG_SIZE - fragOffset);
store.readMiniFragmentNoLock(fragAddr, fragOffset, sublen, os);
return sublen;
}
InodeUpdate update = new InodeUpdate(store, inode, inodeOffset);
try {
long addr = readBlockAddr(inode, inodeOffset, update, fileOffset);
int offset = (int) (fileOffset % BLOCK_SIZE);
sublen = Math.min(sublen, BLOCK_SIZE - offset);
store.readBlockNoLock(addr, offset, os, sublen);
return sublen;
} finally {
update.close();
}
}
/**
* Updates the buffer. Called only from the blob classes.
*/
static void append(byte []inode, int inodeOffset,
BlockStore store, StoreTransaction xa,
byte []buffer, int offset, int length)
throws IOException
{
long currentLength = readLong(inode, inodeOffset);
long newLength = currentLength + length;
writeLong(inode, inodeOffset, newLength);
if (newLength <= INLINE_MAX) {
assert(currentLength == 0);
System.arraycopy(buffer, offset,
inode, (int) (inodeOffset + 8 + currentLength),
length);
}
else if (newLength <= MINI_FRAG_MAX) {
assert(currentLength == 0);
while (length > 0) {
int sublen = length;
if (MINI_FRAG_SIZE < sublen)
sublen = MINI_FRAG_SIZE;
long miniFragAddr = store.allocateMiniFragment();
if (miniFragAddr == 0) {
corrupted(store, L.l("{0} illegal mini fragment", store));
}
writeMiniFragAddr(inode, inodeOffset,
store, xa,
currentLength, miniFragAddr);
Block writeBlock = store.writeMiniFragment(miniFragAddr, 0,
buffer, offset, sublen);
xa.addUpdateBlock(writeBlock);
offset += sublen;
length -= sublen;
currentLength += sublen;
}
}
else {
if (currentLength > 0 && currentLength < MINI_FRAG_MAX)
throw new IllegalStateException(L.l("illegal length transition {0} to {1} because mini-fragmentation must be decided initially.",
currentLength, newLength));
appendBlock(inode, inodeOffset, store, xa,
buffer, offset, length, currentLength);
}
}
private static void appendBlock(byte []inode, int inodeOffset,
BlockStore store, StoreTransaction xa,
byte []buffer, int offset, int length,
long currentLength)
throws IOException
{
InodeUpdate update = new InodeUpdate(store, inode, inodeOffset);
try {
while (length > 0) {
if (currentLength % BLOCK_SIZE != 0) {
long addr = readBlockAddr(inode, inodeOffset,
update,
currentLength);
if (addr == 0) {
corrupted(store, L.l("{0}: inode: illegal block at {1}",
store, currentLength));
}
int blockOffset = (int) (currentLength % BLOCK_SIZE);
int sublen = length;
sublen = Math.min(sublen, BLOCK_SIZE - blockOffset);
Block block = store.writeBlock(addr, blockOffset,
buffer, offset, sublen);
xa.addUpdateBlock(block);
offset += sublen;
length -= sublen;
currentLength += sublen;
}
else {
int sublen = length;
sublen = Math.min(sublen, BLOCK_SIZE);
Block block = store.allocateBlock();
long blockAddr = BlockStore.blockIdToAddress(block.getBlockId());
block.free();
if (blockAddr == 0) {
corrupted(store, L.l("{0}: illegal block", store));
}
writeBlockAddr(inode, inodeOffset,
update, xa,
currentLength, blockAddr);
Block writeBlock = store.writeBlock(blockAddr, 0,
buffer, offset, sublen);
xa.addUpdateBlock(writeBlock);
offset += sublen;
length -= sublen;
currentLength += sublen;
}
}
} finally {
update.close();
}
}
/**
* Reads into a buffer.
*
* @param inode the inode buffer
* @param inodeOffset the offset of the inode data in the buffer
* @param store the owning store
* @param fileOffset the offset into the file to read
* @param buffer the buffer receiving the data
* @param bufferOffset the offset into the receiving buffer
* @param bufferLength the maximum number of chars to read
*
* @return the number of characters read
*/
static int read(byte []inode, int inodeOffset, BlockStore store,
long fileOffset,
char []buffer, int bufferOffset, int bufferLength)
throws IOException
{
long fileLength = readLong(inode, inodeOffset);
int charSublen = (int) (fileLength - fileOffset) / 2;
charSublen = Math.min(bufferLength, charSublen);
if (charSublen <= 0)
return -1;
if (fileLength <= INLINE_MAX) {
int baseOffset = inodeOffset + 8 + (int) fileOffset;
for (int i = 0; i < charSublen; i++) {
char ch = (char) (((inode[baseOffset] & 0xff) << 8)
+ ((inode[baseOffset + 1] & 0xff)));
buffer[bufferOffset + i] = ch;
baseOffset += 2;
}
return charSublen;
}
else if (fileLength <= MINI_FRAG_MAX) {
long fragAddr = readMiniFragAddr(inode, inodeOffset, store, fileOffset);
int fragOffset = (int) (fileOffset % Inode.MINI_FRAG_SIZE);
if (MINI_FRAG_SIZE - fragOffset < 2 * charSublen)
charSublen = (MINI_FRAG_SIZE - fragOffset) / 2;
store.readMiniFragment(fragAddr, fragOffset,
buffer, bufferOffset, charSublen);
return charSublen;
}
InodeUpdate update = new InodeUpdate(store, inode, inodeOffset);
try {
long addr = readBlockAddr(inode, inodeOffset, update, fileOffset);
int offset = (int) (fileOffset % BLOCK_SIZE);
if (BLOCK_SIZE - offset < 2 * charSublen)
charSublen = (BLOCK_SIZE - offset) / 2;
store.readBlock(addr, offset, buffer, bufferOffset, charSublen);
return charSublen;
} finally {
update.close();
}
}
/**
* Updates the buffer. Called only from the clob classes.
*/
static void append(byte []inode, int inodeOffset,
BlockStore store, StoreTransaction xa,
char []buffer, int offset, int charLength)
throws IOException
{
long currentLength = readLong(inode, inodeOffset);
long newLength = currentLength + 2 * charLength;
writeLong(inode, inodeOffset, newLength);
if (newLength <= INLINE_BLOB_SIZE) {
assert(currentLength == 0);
int writeOffset = (int) (inodeOffset + 8 + currentLength);
for (int i = 0; i < charLength; i++) {
char ch = buffer[offset + i];
inode[writeOffset++] = (byte) (ch >> 8);
inode[writeOffset++] = (byte) (ch);
}
}
else if (newLength <= MINI_FRAG_MAX) {
assert(currentLength == 0);
while (charLength > 0) {
int sublen = 2 * charLength;
if (MINI_FRAG_SIZE < sublen)
sublen = MINI_FRAG_SIZE;
long miniFragAddr = store.allocateMiniFragment();
if (miniFragAddr == 0) {
corrupted(store, L.l("{0} illegal mini fragment", store));
}
writeMiniFragAddr(inode, inodeOffset,
store, xa,
currentLength, miniFragAddr);
int charSublen = sublen / 2;
// XXX: store in XA?
Block writeBlock = store.writeMiniFragment(miniFragAddr, 0,
buffer, offset, charSublen);
xa.addUpdateBlock(writeBlock);
offset += charSublen;
charLength -= charSublen;
currentLength += sublen;
}
}
else {
if (currentLength > 0 && currentLength < MINI_FRAG_MAX)
throw new IllegalStateException(L.l("illegal length transition {0} to {1} because mini-fragmentation must be decided initially.",
currentLength, newLength));
appendBlock(inode, inodeOffset, store, xa,
buffer, offset, charLength, currentLength);
}
}
static void appendBlock(byte []inode, int inodeOffset,
BlockStore store, StoreTransaction xa,
char []buffer, int offset, int charLength,
long currentLength)
throws IOException
{
// XXX: theoretically deal with case of appending to inline, although
// the blobs are the only writers and will avoid that case.
InodeUpdate update = new InodeUpdate(store, inode, inodeOffset);
try {
while (charLength > 0) {
if (currentLength % BLOCK_SIZE != 0) {
long addr = readBlockAddr(inode, inodeOffset,
update,
currentLength);
if (addr == 0) {
corrupted(store, store + " inode: illegal block at " + currentLength);
}
int blockOffset = (int) (currentLength % BLOCK_SIZE);
int sublen = 2 * charLength;
sublen = Math.min(BLOCK_SIZE - blockOffset, sublen);
int charSublen = sublen / 2;
Block writeBlock = store.writeBlock(addr, blockOffset,
buffer, offset, charSublen);
xa.addUpdateBlock(writeBlock);
offset += charSublen;
charLength -= charSublen;
currentLength += sublen;
}
else {
int sublen = 2 * charLength;
sublen = Math.min(sublen, BLOCK_SIZE);
int charSublen = sublen / 2;
Block block = store.allocateBlock();
long blockAddr = BlockStore.blockIdToAddress(block.getBlockId());
block.free();
if (blockAddr == 0) {
corrupted(store, store + " inode: illegal block at " + currentLength);
}
Block writeBlock = store.writeBlock(blockAddr, 0,
buffer, offset, charSublen);
xa.addUpdateBlock(writeBlock);
writeBlockAddr(inode, inodeOffset,
update, xa,
currentLength, blockAddr);
offset += charSublen;
charLength -= charSublen;
currentLength += sublen;
}
}
} finally {
update.close();
}
}
/**
* Opens a byte output stream to the inode.
*/
public OutputStream openOutputStream()
{
return new BlobOutputStream(this);
}
/**
* Closes the output stream.
*/
void closeOutputStream()
{
try {
_store.saveAllocation();
} catch (Throwable e) {
log.log(Level.FINER, e.toString(), e);
}
}
/**
* Opens a char reader to the inode.
*/
public Reader openReader()
{
return new ClobReader(this);
}
/**
* Opens a char writer to the inode.
*/
public Writer openWriter()
{
return new ClobWriter(this);
}
/**
* Deletes the inode
*/
public void remove()
{
byte []bytes = new byte[INODE_SIZE];
System.arraycopy(_bytes, 0, bytes, 0, INODE_SIZE);
Arrays.fill(_bytes, 0, INODE_SIZE, (byte) 0);
long length = readLong(bytes, 0);
InodeUpdate update = null;
try {
if (length <= INLINE_BLOB_SIZE || bytes == null) {
return;
}
else if (length <= MINI_FRAG_BLOB_SIZE) {
for (; length > 0; length -= MINI_FRAG_SIZE) {
long fragAddr = readMiniFragAddr(bytes, 0, _store, length - 1);
if ((fragAddr & BlockStore.BLOCK_MASK) == 0) {
String msg = _store + ": inode block " + Long.toHexString(length) + " has 0 fragment";
corrupted(_store, msg);
}
else if (fragAddr < 0) {
String msg = _store + ": inode block " + Long.toHexString(length) + " has invalid fragment " + Long.toHexString(fragAddr);
corrupted(_store, msg);
}
_store.deleteMiniFragment(fragAddr);
}
return;
}
update = new InodeUpdate(_store, bytes, 0);
long indAddr = readLong(bytes, (DIRECT_BLOCKS + 1) * 8);
if (DIRECT_MAX < length
&& ! validateBlockAddr(indAddr, length, BlockStore.ALLOC_INODE_PTR)) {
return;
}
length += BLOCK_SIZE - 1;
length -= length % BLOCK_SIZE;
for (; length > 0; length -= BLOCK_SIZE) {
int blockCount = (int) ((length - 1) / BLOCK_SIZE);
long blockAddr = readBlockAddr(bytes, 0, update, length - 1);
try {
if (! validateBlockAddr(blockAddr, length, BlockStore.ALLOC_DATA, false)) {
return;
}
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
return;
}
_store.deallocateBlock(blockAddr);
int dblBlockCount
= (blockCount - DIRECT_BLOCKS - SINGLE_INDIRECT_BLOCKS);
// remove the double indirect blocks
if (dblBlockCount >= 0
&& dblBlockCount % INDIRECT_BLOCKS == 0) {
int dblIndex = (int) 8 * (dblBlockCount / INDIRECT_BLOCKS
+ SINGLE_INDIRECT_BLOCKS);
blockAddr = update.readBlockLong(indAddr, dblIndex);
if (! validateBlockAddr(blockAddr, length,
BlockStore.ALLOC_INODE_PTR)) {
continue;
}
_store.deallocateBlock(blockAddr);
}
// remove the indirect block
if (blockCount == DIRECT_BLOCKS) {
_store.deallocateBlock(indAddr);
}
}
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
} finally {
if (update != null) {
update.close();
}
// XXX: saved by caller
/*
try {
_store.saveAllocation();
} catch (Throwable e) {
log.log(Level.FINE, e.toString(), e);
}
*/
}
}
private boolean validateBlockAddr(long blockAddr, long length, int allocCode)
{
return validateBlockAddr(blockAddr, length, allocCode, true);
}
private boolean validateBlockAddr(long blockAddr, long length, int allocCode,
boolean fatalOnCorrupt)
{
if ((blockAddr & BlockStore.BLOCK_OFFSET_MASK) != 0) {
String msg = (_store + ": inode block len=" + Long.toHexString(length)
+ " has non-zero offset 0x"
+ Long.toHexString(blockAddr));
warning(msg);
if (fatalOnCorrupt) {
corrupted(_store, msg);
}
return false;
}
else if (blockAddr <= 0) {
String msg = _store + ": inode offset 0x" + Long.toHexString(length) + " has invalid block 0x" + Long.toHexString(blockAddr);
warning(msg);
if (fatalOnCorrupt) {
corrupted(_store, msg);
}
return false;
}
else if (_store.getAllocationByAddress(blockAddr) != allocCode) {
String msg = (_store + ": inode block 0x"
+ Long.toHexString(blockAddr)
+ " len=" + length
+ " has invalid block code (" + _store.getAllocationByAddress(blockAddr) + ")"
+ " expected (" + allocCode + ")");
warning(msg);
if (fatalOnCorrupt) {
corrupted(_store, msg);
}
return false;
}
else {
return true;
}
}
/**
* Validate the inode
*/
public static boolean isValid(BlockStore store, byte []bytes, int offset)
{
long length = readLong(bytes, offset);
InodeUpdate update = null;
try {
if (length < 0 || length > DOUBLE_INDIRECT_MAX) {
String msg = store + ": inode invalid length " + length;
log.warning(msg);
return false;
}
if (length <= INLINE_BLOB_SIZE || bytes == null)
return true;
else if (length <= MINI_FRAG_BLOB_SIZE) {
for (; length > 0; length -= MINI_FRAG_SIZE) {
long fragAddr = readMiniFragAddr(bytes, offset, store, length - 1);
if ((fragAddr & BlockStore.BLOCK_MASK) == 0) {
String msg = store + ": inode block " + Long.toHexString(length) + " has 0 fragment";
log.warning(msg);
return false;
}
else if (fragAddr < 0) {
String msg = store + ": inode block " + Long.toHexString(length) + " has invalid fragment " + Long.toHexString(fragAddr);
log.warning(msg);
return false;
}
}
}
else {
long indAddr = readLong(bytes, offset + (DIRECT_BLOCKS + 1) * 8);
if (DIRECT_MAX < length
&& ! isValidBlockAddr(store, indAddr, length, BlockStore.ALLOC_INODE_PTR)) {
return false;
}
update = new InodeUpdate(store, bytes, offset);
for (; length > 0; length -= BLOCK_SIZE) {
int blockCount = (int) ((length - 1) / BLOCK_SIZE);
long blockAddr = readBlockAddr(bytes, offset, update, length - 1);
if (! isValidBlockAddr(store, blockAddr, length, BlockStore.ALLOC_DATA)) {
return false;
}
int dblBlockCount
= (blockCount - DIRECT_BLOCKS - SINGLE_INDIRECT_BLOCKS);
if (dblBlockCount >= 0
&& dblBlockCount % INDIRECT_BLOCKS == 0) {
int dblIndex = (int) 8 * (dblBlockCount / INDIRECT_BLOCKS
+ SINGLE_INDIRECT_BLOCKS);
blockAddr = update.readBlockLong(indAddr, dblIndex);
if (! isValidBlockAddr(store, blockAddr, length,
BlockStore.ALLOC_INODE_PTR)) {
return false;
}
}
}
}
} catch (Exception e) {
log.log(Level.WARNING, e.toString(), e);
} finally {
if (update != null) {
update.close();
}
}
return true;
}
private static void validateBlockAddr(BlockStore store,
long blockAddr,
int allocCode)
{
if (! isValidBlockAddr(store, blockAddr, 0, allocCode)) {
throw new IllegalStateException(L.l("Invalid block 0x{0} in {1}",
Long.toHexString(blockAddr),
store));
}
}
private static boolean isValidBlockAddr(BlockStore store,
long blockAddr,
long length,
int allocCode)
{
if ((blockAddr & BlockStore.BLOCK_OFFSET_MASK) != 0) {
String msg = (store + ": inode block " + Long.toHexString(length) + " has non-zero offset 0x"
+ Long.toHexString(blockAddr));
log.warning(msg);
return false;
}
else if (blockAddr < BlockStore.BLOCK_SIZE) {
String msg = store + ": inode block " + Long.toHexString(length) + " has invalid block " + Long.toHexString(blockAddr);
log.warning(msg);
return false;
}
else if (store.getAllocationByAddress(blockAddr) != allocCode) {
String msg = (store + ": inode block "
+ Long.toHexString(length)
+ " has invalid block code (" + store.getAllocationByAddress(blockAddr) + ")"
+ " expected (" + allocCode + ")");
log.warning(msg);
return false;
}
else {
return true;
}
}
/**
* Clears the inode.
*/
static void clear(byte []inode, int inodeOffset)
{
int end = inodeOffset + INODE_SIZE;
for (; inodeOffset < end; inodeOffset++)
inode[inodeOffset] = 0;
}
/**
* Returns the fragment id for the given offset.
*/
static long readMiniFragAddr(byte []inode, int inodeOffset,
BlockStore store, long fileOffset)
throws IOException
{
long fragCount = fileOffset / MINI_FRAG_SIZE;
return readLong(inode, (int) (inodeOffset + 8 + 8 * fragCount));
}
/**
* Writes the block id into the inode.
*/
private static void writeMiniFragAddr(byte []inode, int offset,
BlockStore store, StoreTransaction xa,
long fragLength, long fragAddr)
throws IOException
{
int fragCount = (int) (fragLength / MINI_FRAG_SIZE);
if ((fragAddr & BlockStore.BLOCK_MASK) == 0) {
corrupted(store, (store + ": inode block " + fragLength + " has zero value " + fragAddr));
}
writeLong(inode, offset + (fragCount + 1) * 8, fragAddr);
}
/**
* Returns the fragment id for the given offset.
*/
static long readBlockAddr(byte []inode, int inodeOffset,
InodeUpdate update,
long fileOffset)
throws IOException
{
int blockCount = (int) (fileOffset / BLOCK_SIZE);
BlockStore store = update.getStore();
if (fileOffset < DIRECT_MAX) {
return readLong(inode, inodeOffset + (blockCount + 1) * 8);
}
else {
long indAddr = readLong(inode, inodeOffset + (DIRECT_BLOCKS + 1) * 8);
if (indAddr == 0) {
invalid(store, L.l("{0} null block id", store));
}
if (! store.isInodePtrBlock(indAddr)) {
invalid(store, L.l("{0} corrupted or deleted indirect block id 0x{1} type={2}",
store,
Long.toHexString(indAddr),
store.getAllocationByAddress(indAddr)));
}
if (fileOffset < SINGLE_INDIRECT_MAX) {
int blockOffset = 8 * (blockCount - DIRECT_BLOCKS);
long blockAddr = update.readBlockLong(indAddr, blockOffset);
return blockAddr;
}
else if (fileOffset < DOUBLE_INDIRECT_MAX) {
blockCount = blockCount - DIRECT_BLOCKS - SINGLE_INDIRECT_BLOCKS;
int dblBlockCount = blockCount / INDIRECT_BLOCKS;
int dblBlockIndex = 8 * (SINGLE_INDIRECT_BLOCKS + dblBlockCount);
long dblIndAddr = update.readBlockLong(indAddr, dblBlockIndex);
if (dblIndAddr == 0) {
invalid(store, L.l("null indirect block id"));
}
if (! store.isInodePtrBlock(dblIndAddr)) {
invalid(store, L.l("corrupted or deleted dbl-indirect block id"));
}
int blockOffset = 8 * (blockCount % INDIRECT_BLOCKS);
long addr = update.readBlockLong(dblIndAddr, blockOffset);
return addr;
}
else {
throw corrupted(store, L.l("{0} size over {1}M not supported",
store,
(DOUBLE_INDIRECT_MAX / (1024 * 1024))));
}
}
}
/**
* Writes the block id into the inode.
*/
private static void writeBlockAddr(byte []inode, int inodeOffset,
InodeUpdate update,
StoreTransaction xa,
long fileOffset, long blockAddr)
throws IOException
{
BlockStore store = update.getStore();
validateBlockAddr(store, blockAddr, BlockStore.ALLOC_DATA);
int blockCount = (int) (fileOffset / BLOCK_SIZE);
// XXX: not sure if correct, needs XA?
if ((blockAddr & BlockStore.BLOCK_MASK) == 0) {
String msg = store + ": inode block " + blockCount + " writing 0 fragment";
corrupted(store, msg);
}
if (fileOffset < DIRECT_MAX) {
writeLong(inode, inodeOffset + 8 * (blockCount + 1), blockAddr);
}
else if (fileOffset < SINGLE_INDIRECT_MAX) {
long indAddr = readLong(inode, inodeOffset + (DIRECT_BLOCKS + 1) * 8);
if (indAddr == 0) {
Block block = store.allocateIndirectBlock();
indAddr = BlockStore.blockIdToAddress(block.getBlockId());
block.free();
writeLong(inode, inodeOffset + (DIRECT_BLOCKS + 1) * 8, indAddr);
}
validateBlockAddr(store, indAddr, BlockStore.ALLOC_INODE_PTR);
int blockOffset = 8 * (blockCount - DIRECT_BLOCKS);
/*
Block writeBlock = store.writeBlockLong(indAddr, blockOffset, blockAddr);
xa.addUpdateBlock(writeBlock);
*/
update.writeBlockLong(indAddr, blockOffset, blockAddr);
}
else if (fileOffset < DOUBLE_INDIRECT_MAX) {
long indAddr = readLong(inode, inodeOffset + (DIRECT_BLOCKS + 1) * 8);
if (indAddr == 0) {
corrupted(store, L.l("{0} null block id", store));
}
blockCount = blockCount - DIRECT_BLOCKS - SINGLE_INDIRECT_BLOCKS;
int dblBlockCount = blockCount / INDIRECT_BLOCKS;
int dblBlockIndex = 8 * (SINGLE_INDIRECT_BLOCKS + dblBlockCount);
long dblIndAddr = update.readBlockLong(indAddr, dblBlockIndex);
if (dblIndAddr == 0) {
Block block = store.allocateIndirectBlock();
dblIndAddr = BlockStore.blockIdToAddress(block.getBlockId());
block.free();
/*
Block writeBlock = store.writeBlockLong(indAddr, dblBlockIndex, dblIndAddr);
xa.addUpdateBlock(writeBlock);
*/
update.writeBlockLong(indAddr, dblBlockIndex, dblIndAddr);
}
int blockOffset = 8 * (blockCount % INDIRECT_BLOCKS);
/*
Block writeBlock = store.writeBlockLong(dblIndAddr, blockOffset, blockAddr);
xa.addUpdateBlock(writeBlock);
*/
update.writeBlockLong(dblIndAddr, blockOffset, blockAddr);
}
else {
corrupted(store, L.l("{0} size over {1}M not supported",
store,
(DOUBLE_INDIRECT_MAX / (1024 * 1024))));
}
}
private void warning(String msg)
{
log.warning(msg);
}
private static RuntimeException corrupted(BlockStore store, String msg)
{
IllegalStateException e = new IllegalStateException(msg);
e.fillInStackTrace();
log.log(Level.WARNING, e.toString(), e);
e.printStackTrace();
store.fatalCorrupted(msg);
throw e;
}
private static RuntimeException invalid(BlockStore store, String msg)
{
IllegalStateException e = new IllegalStateException(msg);
e.fillInStackTrace();
log.log(Level.WARNING, e.toString(), e);
throw e;
}
/**
* Reads the long.
*/
public static long readLong(byte []buffer, int offset)
{
return (((buffer[offset + 0] & 0xffL) << 56)
+ ((buffer[offset + 1] & 0xffL) << 48)
+ ((buffer[offset + 2] & 0xffL) << 40)
+ ((buffer[offset + 3] & 0xffL) << 32)
+ ((buffer[offset + 4] & 0xffL) << 24)
+ ((buffer[offset + 5] & 0xffL) << 16)
+ ((buffer[offset + 6] & 0xffL) << 8)
+ ((buffer[offset + 7] & 0xffL)));
}
/**
* Writes the long.
*/
public static void writeLong(byte []buffer, int offset, long v)
{
buffer[offset + 0] = (byte) (v >> 56);
buffer[offset + 1] = (byte) (v >> 48);
buffer[offset + 2] = (byte) (v >> 40);
buffer[offset + 3] = (byte) (v >> 32);
buffer[offset + 4] = (byte) (v >> 24);
buffer[offset + 5] = (byte) (v >> 16);
buffer[offset + 6] = (byte) (v >> 8);
buffer[offset + 7] = (byte) (v);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy