0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.db.store;
0031:
0032: import com.caucho.util.L10N;
0033: import com.caucho.util.Log;
0034: import com.caucho.vfs.OutputStreamWithBuffer;
0035: import com.caucho.vfs.TempCharBuffer;
0036:
0037: import java.io.IOException;
0038: import java.io.InputStream;
0039: import java.io.OutputStream;
0040: import java.io.Reader;
0041: import java.io.Writer;
0042: import java.util.logging.Level;
0043: import java.util.logging.Logger;
0044:
0045: /**
0046: * Represents the indexes for a BLOB or CLOB.
0047: *
0048: * The inode contains 16 long values
0049: * <pre>
0050: * 0) length of the saved file
0051: * 1-14) direct fragment addresses (to 112k)
0052: * 15) pointer to the indirect block
0053: * </pre>
0054: *
0055: * <h3>Inline storage (120)</h3>
0056: *
0057: * If the length of the blob is less than 120, the blob is stored directly
0058: * in the inode.
0059: *
0060: * <h3>mini fragment storage (3840)</h3>
0061: *
0062: * If the length of the blob is less than 3840, the blob is stored
0063: * in mini-fragments of size 256 pointed by the inode's addresses.
0064: *
0065: * The maximum wasted space for mini-fragment storage is 255 bytes.
0066: *
0067: * <h3>indirect storage</h3>
0068: *
0069: * The indirect block (an 8k fragment) itself is divided into sections:
0070: * <pre>
0071: * 0-511) single indirect fragment addresses (4M, 2^12)
0072: * 512-767) double indirect block addresses (128G, 2^37)
0073: * 768-1023) triple indirect fragment addresses (to 1P, 2^50)
0074: * </pre>
0075: */
0076: public class Inode {
0077: private static final L10N L = new L10N(Inode.class);
0078: private static final Logger log = Logger.getLogger(Inode.class
0079: .getName());
0080:
0081: public static final int INODE_SIZE = 128;
0082: public static final int INLINE_BLOB_SIZE = 120;
0083: public static final int BLOCK_SIZE = Store.BLOCK_SIZE;
0084: public static final int INODE_BLOCK_SIZE = Store.FRAGMENT_SIZE;
0085: public static final int FRAGMENT_SIZE = Store.FRAGMENT_SIZE;
0086:
0087: public static final int MINI_FRAG_SIZE = Store.MINI_FRAG_SIZE;
0088:
0089: public static final int MINI_FRAG_BLOB_SIZE = (INLINE_BLOB_SIZE / 8)
0090: * MINI_FRAG_SIZE;
0091:
0092: public static final int INDIRECT_BLOCKS = INODE_BLOCK_SIZE / 8;
0093:
0094: // direct addresses are stored in the inode itself (112k of data).
0095: public static final int DIRECT_BLOCKS = 14;
0096: // single indirect addresses are stored in the indirect block (4M data)
0097: public static final int SINGLE_INDIRECT_BLOCKS = 512;
0098: // double indirect addresses (2^37 = 128G data)
0099: public static final int DOUBLE_INDIRECT_BLOCKS = 256;
0100: // triple indirect addresses (2^50 = 2P data)
0101: public static final int TRIPLE_INDIRECT_BLOCKS = 256;
0102:
0103: public static final long INLINE_MAX = 120;
0104:
0105: public static final int MINI_FRAG_MAX = (INLINE_BLOB_SIZE / 8)
0106: * MINI_FRAG_SIZE;
0107:
0108: public static final long DIRECT_MAX = FRAGMENT_SIZE * DIRECT_BLOCKS;
0109:
0110: public static final long SINGLE_INDIRECT_MAX = DIRECT_MAX
0111: + SINGLE_INDIRECT_BLOCKS * FRAGMENT_SIZE;
0112:
0113: public static final long FRAGMENT_MAX = SINGLE_INDIRECT_MAX;
0114:
0115: public static final long DOUBLE_INDIRECT_MAX = (SINGLE_INDIRECT_MAX + DOUBLE_INDIRECT_BLOCKS
0116: * (BLOCK_SIZE / 8) * BLOCK_SIZE);
0117:
0118: private static final byte[] NULL_BYTES = new byte[INODE_SIZE];
0119:
0120: private Store _store;
0121: private StoreTransaction _xa;
0122:
0123: private byte[] _bytes = new byte[INODE_SIZE];
0124:
0125: public Inode() {
0126: }
0127:
0128: public Inode(Store store, StoreTransaction xa) {
0129: _store = store;
0130: _xa = xa;
0131: }
0132:
0133: public Inode(Store store) {
0134: this (store, RawTransaction.create());
0135: }
0136:
0137: /**
0138: * Returns the backing store.
0139: */
0140: public Store getStore() {
0141: return _store;
0142: }
0143:
0144: /**
0145: * Returns the buffer.
0146: */
0147: public byte[] getBuffer() {
0148: return _bytes;
0149: }
0150:
0151: /**
0152: * Returns the length.
0153: */
0154: public long getLength() {
0155: return readLong(_bytes, 0);
0156: }
0157:
0158: public void init(Store store, StoreTransaction xa, byte[] buffer,
0159: int offset) {
0160: _store = store;
0161: _xa = xa;
0162:
0163: System.arraycopy(buffer, offset, _bytes, 0, _bytes.length);
0164: }
0165:
0166: /**
0167: * Opens a read stream to the inode.
0168: */
0169: public InputStream openInputStream() {
0170: return new BlobInputStream(this );
0171: }
0172:
0173: /**
0174: * Writes the inode value to a stream.
0175: */
0176: public void writeToStream(OutputStreamWithBuffer os)
0177: throws IOException {
0178: writeToStream(os, 0, Long.MAX_VALUE / 2);
0179: }
0180:
0181: /**
0182: * Writes the inode value to a stream.
0183: */
0184: public void writeToStream(OutputStreamWithBuffer os, long offset,
0185: long length) throws IOException {
0186: byte[] buffer = os.getBuffer();
0187: int writeLength = buffer.length;
0188: int writeOffset = os.getBufferOffset();
0189:
0190: while (length > 0) {
0191: int sublen = writeLength - writeOffset;
0192:
0193: if (sublen == 0) {
0194: buffer = os.nextBuffer(writeOffset);
0195: writeOffset = os.getBufferOffset();
0196: sublen = writeLength - writeOffset;
0197: }
0198:
0199: if (length < sublen)
0200: sublen = (int) length;
0201:
0202: int len = read(_bytes, 0, _store, offset, buffer,
0203: writeOffset, sublen);
0204:
0205: if (len <= 0)
0206: break;
0207:
0208: writeOffset += len;
0209: offset += len;
0210: length -= len;
0211: }
0212:
0213: os.setBufferOffset(writeOffset);
0214: }
0215:
0216: /**
0217: * Writes the inode value to a stream.
0218: */
0219: public void writeToWriter(Writer writer) throws IOException {
0220: TempCharBuffer tempBuffer = TempCharBuffer.allocate();
0221:
0222: char[] buffer = tempBuffer.getBuffer();
0223: int writeLength = buffer.length;
0224: long offset = 0;
0225:
0226: while (true) {
0227: int sublen = writeLength;
0228:
0229: int len = read(_bytes, 0, _store, offset, buffer, 0, sublen);
0230:
0231: if (len <= 0)
0232: break;
0233:
0234: writer.write(buffer, 0, len);
0235:
0236: offset += 2 * len;
0237: }
0238:
0239: TempCharBuffer.free(tempBuffer);
0240: }
0241:
0242: /**
0243: * Reads into a buffer.
0244: *
0245: * @param inode the inode buffer
0246: * @param inodeOffset the offset of the inode data in the buffer
0247: * @param store the owning store
0248: * @param fileOffset the offset into the file to read
0249: * @param buffer the buffer receiving the data
0250: * @param bufferOffset the offset into the receiving buffer
0251: * @param bufferLength the maximum number of bytes to read
0252: *
0253: * @return the number of bytes read
0254: */
0255: static int read(byte[] inode, int inodeOffset, Store store,
0256: long fileOffset, byte[] buffer, int bufferOffset,
0257: int bufferLength) throws IOException {
0258: long fileLength = readLong(inode, inodeOffset);
0259:
0260: int sublen = bufferLength;
0261: if (fileLength - fileOffset < sublen)
0262: sublen = (int) (fileLength - fileOffset);
0263:
0264: if (sublen <= 0)
0265: return -1;
0266:
0267: if (fileLength <= INLINE_MAX) {
0268: System.arraycopy(inode, inodeOffset + 8 + (int) fileOffset,
0269: buffer, bufferOffset, sublen);
0270:
0271: return sublen;
0272: } else if (fileLength <= MINI_FRAG_MAX) {
0273: long fragAddr = readMiniFragAddr(inode, inodeOffset, store,
0274: fileOffset);
0275: int fragOffset = (int) (fileOffset % MINI_FRAG_SIZE);
0276:
0277: if (MINI_FRAG_SIZE - fragOffset < sublen)
0278: sublen = MINI_FRAG_SIZE - fragOffset;
0279:
0280: store.readMiniFragment(fragAddr, fragOffset, buffer,
0281: bufferOffset, sublen);
0282:
0283: return sublen;
0284: } else if (fileLength <= FRAGMENT_MAX) {
0285: long fragAddr = readFragmentAddr(inode, inodeOffset, store,
0286: fileOffset);
0287: int fragOffset = (int) (fileOffset % FRAGMENT_SIZE);
0288:
0289: if (FRAGMENT_SIZE - fragOffset < sublen)
0290: sublen = FRAGMENT_SIZE - fragOffset;
0291:
0292: store.readFragment(fragAddr, fragOffset, buffer,
0293: bufferOffset, sublen);
0294:
0295: return sublen;
0296: } else {
0297: long addr = readBlockAddr(inode, inodeOffset, store,
0298: fileOffset);
0299: int offset = (int) ((fileOffset - FRAGMENT_MAX) % BLOCK_SIZE);
0300:
0301: if (BLOCK_SIZE - offset < sublen)
0302: sublen = BLOCK_SIZE - offset;
0303:
0304: store.readBlock(addr, offset, buffer, bufferOffset, sublen);
0305:
0306: return sublen;
0307: }
0308: }
0309:
0310: /**
0311: * Updates the buffer. Called only from the blob classes.
0312: */
0313: static void append(byte[] inode, int inodeOffset, Store store,
0314: StoreTransaction xa, byte[] buffer, int offset, int length)
0315: throws IOException {
0316: long currentLength = readLong(inode, inodeOffset);
0317: long newLength = currentLength + length;
0318:
0319: writeLong(inode, inodeOffset, newLength);
0320:
0321: if (newLength <= INLINE_MAX) {
0322: System.arraycopy(buffer, offset, inode,
0323: (int) (inodeOffset + 8 + currentLength), length);
0324: } else if (newLength <= MINI_FRAG_MAX) {
0325: while (length > 0) {
0326: int sublen = length;
0327:
0328: if (MINI_FRAG_SIZE < sublen)
0329: sublen = MINI_FRAG_SIZE;
0330:
0331: long miniFragAddr = store.allocateMiniFragment(xa);
0332:
0333: if (miniFragAddr == 0)
0334: throw new IllegalStateException(L
0335: .l("illegal mini fragment"));
0336:
0337: writeMiniFragAddr(inode, inodeOffset, store, xa,
0338: currentLength, miniFragAddr);
0339:
0340: store.writeMiniFragment(xa, miniFragAddr, 0, buffer,
0341: offset, sublen);
0342:
0343: offset += sublen;
0344: length -= sublen;
0345: currentLength += sublen;
0346: }
0347: } else {
0348: if (currentLength < FRAGMENT_MAX) {
0349: int sublen = length;
0350:
0351: if (FRAGMENT_MAX - currentLength < sublen)
0352: sublen = (int) (FRAGMENT_MAX - currentLength);
0353:
0354: appendFragment(inode, inodeOffset, store, xa, buffer,
0355: offset, length, currentLength);
0356:
0357: offset += sublen;
0358: length -= sublen;
0359: currentLength += sublen;
0360: }
0361:
0362: if (length > 0) {
0363: appendBlock(inode, inodeOffset, store, xa, buffer,
0364: offset, length, currentLength);
0365: }
0366: }
0367: }
0368:
0369: private static void appendFragment(byte[] inode, int inodeOffset,
0370: Store store, StoreTransaction xa, byte[] buffer,
0371: int offset, int length, long currentLength)
0372: throws IOException {
0373: // XXX: theoretically deal with case of appending to inline, although
0374: // the blobs are the only writers and will avoid that case.
0375:
0376: while (length > 0 && currentLength < FRAGMENT_MAX) {
0377: if (currentLength % FRAGMENT_SIZE != 0) {
0378: long fragAddr = readFragmentAddr(inode, inodeOffset,
0379: store, currentLength);
0380:
0381: if (fragAddr == 0)
0382: throw new IllegalStateException(
0383: "inode: illegal fragment at "
0384: + currentLength);
0385:
0386: int fragOffset = (int) (currentLength % INODE_BLOCK_SIZE);
0387: int sublen = length;
0388:
0389: if (INODE_BLOCK_SIZE - fragOffset < sublen)
0390: sublen = INODE_BLOCK_SIZE - fragOffset;
0391:
0392: store.writeFragment(xa, fragAddr, fragOffset, buffer,
0393: offset, sublen);
0394:
0395: offset += sublen;
0396: length -= sublen;
0397:
0398: currentLength += sublen;
0399: } else {
0400: int sublen = length;
0401:
0402: if (FRAGMENT_SIZE < sublen)
0403: sublen = FRAGMENT_SIZE;
0404:
0405: long fragAddr = store.allocateFragment(xa);
0406:
0407: if (fragAddr == 0)
0408: throw new IllegalStateException(L
0409: .l("illegal fragment"));
0410:
0411: writeFragmentAddr(inode, inodeOffset, store, xa,
0412: currentLength, fragAddr);
0413:
0414: store.writeFragment(xa, fragAddr, 0, buffer, offset,
0415: sublen);
0416:
0417: offset += sublen;
0418: length -= sublen;
0419:
0420: currentLength += sublen;
0421: }
0422: }
0423: }
0424:
0425: private static void appendBlock(byte[] inode, int inodeOffset,
0426: Store store, StoreTransaction xa, byte[] buffer,
0427: int offset, int length, long currentLength)
0428: throws IOException {
0429: // XXX: theoretically deal with case of appending to inline, although
0430: // the blobs are the only writers and will avoid that case.
0431:
0432: while (length > 0) {
0433: if ((currentLength - FRAGMENT_MAX) % BLOCK_SIZE != 0) {
0434: long addr = readBlockAddr(inode, inodeOffset, store,
0435: currentLength);
0436:
0437: if (addr == 0)
0438: throw new IllegalStateException(
0439: "inode: illegal block at " + currentLength);
0440:
0441: int blockOffset = (int) ((currentLength - FRAGMENT_MAX) % BLOCK_SIZE);
0442: int sublen = length;
0443:
0444: if (BLOCK_SIZE - blockOffset < sublen)
0445: sublen = BLOCK_SIZE - blockOffset;
0446:
0447: store.writeBlock(xa, addr, blockOffset, buffer, offset,
0448: sublen);
0449:
0450: offset += sublen;
0451: length -= sublen;
0452:
0453: currentLength += sublen;
0454: } else {
0455: int sublen = length;
0456:
0457: if (BLOCK_SIZE < sublen)
0458: sublen = BLOCK_SIZE;
0459:
0460: long blockAddr = store.allocateFragment(xa);
0461:
0462: if (blockAddr == 0)
0463: throw new IllegalStateException(L
0464: .l("illegal fragment"));
0465:
0466: writeBlockAddr(inode, inodeOffset, store, xa,
0467: currentLength, blockAddr);
0468:
0469: store.writeBlock(xa, blockAddr, 0, buffer, offset,
0470: sublen);
0471:
0472: offset += sublen;
0473: length -= sublen;
0474:
0475: currentLength += sublen;
0476: }
0477: }
0478: }
0479:
0480: /**
0481: * Reads into a buffer.
0482: *
0483: * @param inode the inode buffer
0484: * @param inodeOffset the offset of the inode data in the buffer
0485: * @param store the owning store
0486: * @param fileOffset the offset into the file to read
0487: * @param buffer the buffer receiving the data
0488: * @param bufferOffset the offset into the receiving buffer
0489: * @param bufferLength the maximum number of chars to read
0490: *
0491: * @return the number of characters read
0492: */
0493: static int read(byte[] inode, int inodeOffset, Store store,
0494: long fileOffset, char[] buffer, int bufferOffset,
0495: int bufferLength) throws IOException {
0496: long fileLength = readLong(inode, inodeOffset);
0497:
0498: int sublen = (int) (fileLength - fileOffset) / 2;
0499: if (bufferLength < sublen)
0500: sublen = bufferLength;
0501:
0502: if (sublen <= 0)
0503: return -1;
0504:
0505: if (fileLength <= INLINE_MAX) {
0506: int baseOffset = inodeOffset + 8 + (int) fileOffset;
0507:
0508: for (int i = 0; i < sublen; i++) {
0509: char ch = (char) (((inode[baseOffset] & 0xff) << 8) + ((inode[baseOffset + 1] & 0xff)));
0510:
0511: buffer[bufferOffset + i] = ch;
0512:
0513: baseOffset += 2;
0514: }
0515:
0516: return sublen;
0517: } else if (fileLength <= MINI_FRAG_MAX) {
0518: long fragAddr = readMiniFragAddr(inode, inodeOffset, store,
0519: fileOffset);
0520: int fragOffset = (int) (fileOffset % Inode.MINI_FRAG_SIZE);
0521:
0522: if (MINI_FRAG_SIZE - fragOffset < 2 * sublen)
0523: sublen = (MINI_FRAG_SIZE - fragOffset) / 2;
0524:
0525: store.readMiniFragment(fragAddr, fragOffset, buffer,
0526: bufferOffset, sublen);
0527:
0528: return sublen;
0529: } else if (fileLength <= FRAGMENT_MAX) {
0530: long fragAddr = readFragmentAddr(inode, inodeOffset, store,
0531: fileOffset);
0532: int fragOffset = (int) (fileOffset % Inode.INODE_BLOCK_SIZE);
0533:
0534: if (FRAGMENT_SIZE - fragOffset < 2 * sublen)
0535: sublen = (FRAGMENT_SIZE - fragOffset) / 2;
0536:
0537: store.readFragment(fragAddr, fragOffset, buffer,
0538: bufferOffset, sublen);
0539:
0540: return sublen;
0541: } else {
0542: long addr = readBlockAddr(inode, inodeOffset, store,
0543: fileOffset);
0544: int offset = (int) ((fileOffset - FRAGMENT_MAX) % BLOCK_SIZE);
0545:
0546: if (BLOCK_SIZE - offset < sublen)
0547: sublen = BLOCK_SIZE - offset;
0548:
0549: store.readBlock(addr, offset, buffer, bufferOffset, sublen);
0550:
0551: return sublen;
0552: }
0553: }
0554:
0555: /**
0556: * Updates the buffer. Called only from the clob classes.
0557: */
0558: static void append(byte[] inode, int inodeOffset, Store store,
0559: StoreTransaction xa, char[] buffer, int offset, int length)
0560: throws IOException {
0561: long currentLength = readLong(inode, inodeOffset);
0562: long newLength = currentLength + length;
0563:
0564: writeLong(inode, inodeOffset, newLength);
0565:
0566: if (newLength <= INLINE_BLOB_SIZE) {
0567: int writeOffset = (int) (inodeOffset + 8 + currentLength);
0568:
0569: for (int i = 0; i < length; i++) {
0570: char ch = buffer[offset + i];
0571:
0572: inode[writeOffset++] = (byte) (ch >> 8);
0573: inode[writeOffset++] = (byte) (ch);
0574: }
0575: } else {
0576: // XXX: theoretically deal with case of appending to inline, although
0577: // the blobs are the only writers and will avoid that case.
0578:
0579: if (currentLength % FRAGMENT_SIZE != 0) {
0580: long fragAddr = readFragmentAddr(inode, inodeOffset,
0581: store, currentLength);
0582:
0583: int fragOffset = (int) (currentLength % FRAGMENT_SIZE);
0584: int sublen = 2 * length;
0585:
0586: if (INODE_BLOCK_SIZE - fragOffset < sublen)
0587: sublen = INODE_BLOCK_SIZE - fragOffset;
0588:
0589: store.writeFragment(xa, fragAddr, fragOffset, buffer,
0590: offset, sublen);
0591:
0592: offset += sublen / 2;
0593: length -= sublen / 2;
0594:
0595: currentLength += sublen;
0596: }
0597:
0598: while (length > 0) {
0599: int sublen = 2 * length;
0600:
0601: if (INODE_BLOCK_SIZE < sublen)
0602: sublen = INODE_BLOCK_SIZE;
0603:
0604: long fragAddr = store.allocateFragment(xa);
0605:
0606: writeFragmentAddr(inode, inodeOffset, store, xa,
0607: currentLength, fragAddr);
0608:
0609: store.writeFragment(xa, fragAddr, 0, buffer, offset,
0610: sublen);
0611:
0612: offset += sublen / 2;
0613: length -= sublen / 2;
0614:
0615: currentLength += sublen;
0616: }
0617: }
0618: }
0619:
0620: private void appendFragment(byte[] inode, int inodeOffset,
0621: Store store, StoreTransaction xa, char[] buffer,
0622: int offset, int length, long currentLength)
0623: throws IOException {
0624: // XXX: theoretically deal with case of appending to inline, although
0625: // the blobs are the only writers and will avoid that case.
0626:
0627: while (length > 0 && currentLength < FRAGMENT_MAX) {
0628: if (currentLength % FRAGMENT_SIZE != 0) {
0629: long fragAddr = readFragmentAddr(inode, inodeOffset,
0630: store, currentLength);
0631:
0632: if (fragAddr == 0)
0633: throw new IllegalStateException(
0634: "inode: illegal fragment at "
0635: + currentLength);
0636:
0637: int fragOffset = (int) (currentLength % INODE_BLOCK_SIZE);
0638: int sublen = 2 * length;
0639:
0640: if (INODE_BLOCK_SIZE - fragOffset < sublen)
0641: sublen = INODE_BLOCK_SIZE - fragOffset;
0642:
0643: store.writeFragment(xa, fragAddr, fragOffset, buffer,
0644: offset, sublen);
0645:
0646: offset += sublen / 2;
0647: length -= sublen / 2;
0648:
0649: currentLength += sublen;
0650: } else {
0651: int sublen = 2 * length;
0652:
0653: if (FRAGMENT_SIZE < sublen)
0654: sublen = FRAGMENT_SIZE;
0655:
0656: long fragAddr = store.allocateFragment(xa);
0657:
0658: if (fragAddr == 0)
0659: throw new IllegalStateException(L
0660: .l("illegal fragment"));
0661:
0662: writeFragmentAddr(inode, inodeOffset, store, xa,
0663: currentLength, fragAddr);
0664:
0665: store.writeFragment(xa, fragAddr, 0, buffer, offset,
0666: sublen);
0667:
0668: offset += sublen / 2;
0669: length -= sublen / 2;
0670:
0671: currentLength += sublen;
0672: }
0673: }
0674: }
0675:
0676: private void appendBlock(byte[] inode, int inodeOffset,
0677: Store store, StoreTransaction xa, char[] buffer,
0678: int offset, int length, long currentLength)
0679: throws IOException {
0680: // XXX: theoretically deal with case of appending to inline, although
0681: // the blobs are the only writers and will avoid that case.
0682:
0683: while (length > 0) {
0684: if ((currentLength - FRAGMENT_MAX) % BLOCK_SIZE != 0) {
0685: long addr = readBlockAddr(inode, inodeOffset, store,
0686: currentLength);
0687:
0688: if (addr == 0)
0689: throw new IllegalStateException(
0690: "inode: illegal block at " + currentLength);
0691:
0692: int blockOffset = (int) ((currentLength - FRAGMENT_MAX) % BLOCK_SIZE);
0693: int sublen = 2 * length;
0694:
0695: if (BLOCK_SIZE - blockOffset < sublen)
0696: sublen = BLOCK_SIZE - blockOffset;
0697:
0698: store.writeBlock(xa, addr, blockOffset, buffer, offset,
0699: sublen);
0700:
0701: offset += sublen / 2;
0702: length -= sublen / 2;
0703:
0704: currentLength += sublen;
0705: } else {
0706: int sublen = 2 * length;
0707:
0708: if (BLOCK_SIZE < sublen)
0709: sublen = BLOCK_SIZE;
0710:
0711: long blockAddr = store.allocateFragment(xa);
0712:
0713: if (blockAddr == 0)
0714: throw new IllegalStateException(L
0715: .l("illegal fragment"));
0716:
0717: writeBlockAddr(inode, inodeOffset, store, xa,
0718: currentLength, blockAddr);
0719:
0720: store.writeBlock(xa, blockAddr, 0, buffer, offset,
0721: sublen);
0722:
0723: offset += sublen / 2;
0724: length -= sublen / 2;
0725:
0726: currentLength += sublen;
0727: }
0728: }
0729: }
0730:
0731: /**
0732: * Opens a byte output stream to the inode.
0733: */
0734: public OutputStream openOutputStream() {
0735: return new BlobOutputStream(this );
0736: }
0737:
0738: /**
0739: * Closes the output stream.
0740: */
0741: void closeOutputStream() {
0742: try {
0743: _store.saveAllocation();
0744: } catch (Throwable e) {
0745: log.log(Level.FINER, e.toString(), e);
0746: }
0747: }
0748:
0749: /**
0750: * Opens a char reader to the inode.
0751: */
0752: public Reader openReader() {
0753: return new ClobReader(this );
0754: }
0755:
0756: /**
0757: * Opens a char writer to the inode.
0758: */
0759: public Writer openWriter() {
0760: return new ClobWriter(this );
0761: }
0762:
0763: /**
0764: * Deletes the inode
0765: */
0766: public void remove() {
0767: synchronized (_bytes) {
0768: long length = readLong(_bytes, 0);
0769:
0770: byte[] bytes = _bytes;
0771:
0772: try {
0773: if (length <= INLINE_BLOB_SIZE || bytes == null)
0774: return;
0775: else if (length <= MINI_FRAG_BLOB_SIZE) {
0776: for (; length > 0; length -= MINI_FRAG_SIZE) {
0777: long fragAddr = readMiniFragAddr(bytes, 0,
0778: _store, length - 1);
0779:
0780: if ((fragAddr & Store.BLOCK_MASK) == 0) {
0781: String msg = _store + ": inode block "
0782: + Long.toHexString(length)
0783: + " has 0 fragment";
0784: throw stateError(msg);
0785: } else if (fragAddr < 0) {
0786: String msg = _store + ": inode block "
0787: + Long.toHexString(length)
0788: + " has invalid fragment "
0789: + Long.toHexString(fragAddr);
0790:
0791: throw stateError(msg);
0792: }
0793:
0794: _store.deleteMiniFragment(_xa, fragAddr);
0795: }
0796: } else {
0797: long initLength = length;
0798: for (; length > 0; length -= INODE_BLOCK_SIZE) {
0799: long fragAddr = readFragmentAddr(bytes, 0,
0800: _store, length - 1);
0801:
0802: if ((fragAddr & Store.BLOCK_MASK) == 0) {
0803: String msg = _store + ": inode block "
0804: + Long.toHexString(length)
0805: + " has 0 fragment";
0806: throw stateError(msg);
0807: } else if (fragAddr < 0) {
0808: String msg = _store + ": inode block "
0809: + Long.toHexString(length)
0810: + " has invalid fragment "
0811: + Long.toHexString(fragAddr);
0812:
0813: throw stateError(msg);
0814: }
0815:
0816: _store.deleteFragment(_xa, fragAddr);
0817:
0818: int fragCount = (int) ((length - 1) / INODE_BLOCK_SIZE);
0819:
0820: int dblFragCount = fragCount - DIRECT_BLOCKS
0821: - SINGLE_INDIRECT_BLOCKS;
0822:
0823: // remove the double indirect blocks
0824: if (dblFragCount >= 0
0825: && dblFragCount % INDIRECT_BLOCKS == 0) {
0826: fragAddr = readLong(bytes,
0827: (DIRECT_BLOCKS + 1) * 8);
0828:
0829: int dblIndex = (int) (fragCount / INDIRECT_BLOCKS);
0830:
0831: fragAddr = _store.readFragmentLong(
0832: fragAddr, dblIndex);
0833:
0834: if (fragAddr != 0)
0835: _store.deleteFragment(_xa, fragAddr);
0836: }
0837:
0838: // remove the indirect blocks
0839: if (fragCount == DIRECT_BLOCKS) {
0840: fragAddr = readLong(bytes,
0841: (DIRECT_BLOCKS + 1) * 8);
0842:
0843: if (fragAddr != 0) {
0844: _store.deleteFragment(_xa, fragAddr);
0845: }
0846: }
0847: }
0848: }
0849: } catch (Throwable e) {
0850: log.log(Level.WARNING, e.toString(), e);
0851: } finally {
0852: System.arraycopy(NULL_BYTES, 0, _bytes, 0,
0853: NULL_BYTES.length);
0854:
0855: try {
0856: _store.saveAllocation();
0857: } catch (Throwable e) {
0858: log.log(Level.FINE, e.toString(), e);
0859: }
0860: }
0861: }
0862: }
0863:
0864: /**
0865: * Clears the inode.
0866: */
0867: static void clear(byte[] inode, int inodeOffset) {
0868: int end = inodeOffset + INODE_SIZE;
0869:
0870: for (; inodeOffset < end; inodeOffset++)
0871: inode[inodeOffset] = 0;
0872: }
0873:
0874: /**
0875: * Returns the fragment id for the given offset.
0876: */
0877: static long readMiniFragAddr(byte[] inode, int inodeOffset,
0878: Store store, long fileOffset) throws IOException {
0879: long fragCount = fileOffset / MINI_FRAG_SIZE;
0880:
0881: return readLong(inode, (int) (inodeOffset + 8 + 8 * fragCount));
0882: }
0883:
0884: /**
0885: * Writes the block id into the inode.
0886: */
0887: private static void writeMiniFragAddr(byte[] inode, int offset,
0888: Store store, StoreTransaction xa, long fragLength,
0889: long fragAddr) throws IOException {
0890: int fragCount = (int) (fragLength / MINI_FRAG_SIZE);
0891:
0892: if ((fragAddr & Store.BLOCK_MASK) == 0) {
0893: throw new IllegalStateException(store + ": inode block "
0894: + fragLength + " has zero value " + fragAddr);
0895: }
0896:
0897: writeLong(inode, offset + (fragCount + 1) * 8, fragAddr);
0898: }
0899:
0900: /**
0901: * Returns the fragment id for the given offset.
0902: */
0903: static long readFragmentAddr(byte[] inode, int inodeOffset,
0904: Store store, long fileOffset) throws IOException {
0905: long fragCount = fileOffset / INODE_BLOCK_SIZE;
0906:
0907: if (fragCount < DIRECT_BLOCKS)
0908: return readLong(inode,
0909: (int) (inodeOffset + 8 + 8 * fragCount));
0910: else if (fragCount < DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS) {
0911: long indirectAddr;
0912: indirectAddr = readLong(inode, inodeOffset
0913: + (DIRECT_BLOCKS + 1) * 8);
0914:
0915: if (indirectAddr == 0)
0916: throw new IllegalStateException(L.l("null block id"));
0917:
0918: int offset = (int) (8 * (fragCount - DIRECT_BLOCKS));
0919:
0920: long fragAddr = store
0921: .readFragmentLong(indirectAddr, offset);
0922:
0923: return fragAddr;
0924: } else if (fragCount < (DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS + DOUBLE_INDIRECT_BLOCKS
0925: * INDIRECT_BLOCKS)) {
0926: long indirectAddr;
0927: indirectAddr = readLong(inode, inodeOffset
0928: + (DIRECT_BLOCKS + 1) * 8);
0929:
0930: if (indirectAddr == 0)
0931: throw new IllegalStateException(L.l("null block id"));
0932:
0933: fragCount -= DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS;
0934:
0935: int index = (int) (fragCount / INDIRECT_BLOCKS);
0936:
0937: long doubleIndirectAddr = store.readFragmentLong(
0938: indirectAddr, index);
0939:
0940: int offset = (int) (8 * (fragCount % INDIRECT_BLOCKS));
0941:
0942: return store.readFragmentLong(doubleIndirectAddr, offset);
0943: } else
0944: throw new IllegalStateException(L
0945: .l("Can't yet support data over 64M"));
0946: }
0947:
0948: /**
0949: * Writes the block id into the inode.
0950: */
0951: private static void writeFragmentAddr(byte[] inode, int offset,
0952: Store store, StoreTransaction xa, long fragLength,
0953: long fragAddr) throws IOException {
0954: int fragCount = (int) (fragLength / Store.FRAGMENT_SIZE);
0955:
0956: // XXX: not sure if correct, needs XA?
0957: if ((fragAddr & Store.BLOCK_MASK) == 0) {
0958: String msg = store + ": inode block " + fragCount
0959: + " writing 0 fragment";
0960: throw stateError(msg);
0961: }
0962:
0963: if (fragCount < DIRECT_BLOCKS) {
0964: writeLong(inode, offset + (fragCount + 1) * 8, fragAddr);
0965: } else if (fragCount < DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS) {
0966: long indAddr = readLong(inode, offset + (DIRECT_BLOCKS + 1)
0967: * 8);
0968:
0969: if (indAddr == 0) {
0970: indAddr = store.allocateFragment(xa);
0971:
0972: writeLong(inode, offset + (DIRECT_BLOCKS + 1) * 8,
0973: indAddr);
0974: }
0975:
0976: int fragOffset = 8 * (fragCount - DIRECT_BLOCKS);
0977:
0978: store.writeFragmentLong(xa, indAddr, fragOffset, fragAddr);
0979: } else if (fragCount < (DIRECT_BLOCKS + SINGLE_INDIRECT_BLOCKS + DOUBLE_INDIRECT_BLOCKS
0980: * INDIRECT_BLOCKS)) {
0981: long indAddr = readLong(inode, offset + (DIRECT_BLOCKS + 1)
0982: * 8);
0983:
0984: if (indAddr == 0) {
0985: indAddr = store.allocateFragment(xa);
0986:
0987: writeLong(inode, offset + (DIRECT_BLOCKS + 1) * 8,
0988: indAddr);
0989: }
0990:
0991: int count = fragCount - DIRECT_BLOCKS
0992: - SINGLE_INDIRECT_BLOCKS;
0993:
0994: int dblIndCount = count / INDIRECT_BLOCKS;
0995:
0996: long dblIndAddr = store.readFragmentLong(indAddr,
0997: dblIndCount * 8);
0998:
0999: if (dblIndAddr == 0) {
1000: dblIndAddr = store.allocateFragment(xa);
1001:
1002: store.writeFragmentLong(xa, indAddr, dblIndCount * 8,
1003: dblIndAddr);
1004: }
1005:
1006: int fragOffset = 8 * (count % INDIRECT_BLOCKS);
1007:
1008: store.writeFragmentLong(xa, dblIndAddr, fragOffset,
1009: fragAddr);
1010: } else
1011: throw new IllegalStateException(L
1012: .l("Can't yet support data over 64M"));
1013: }
1014:
1015: /**
1016: * Returns the fragment id for the given offset.
1017: */
1018: static long readBlockAddr(byte[] inode, int inodeOffset,
1019: Store store, long fileOffset) throws IOException {
1020: if (fileOffset <= FRAGMENT_MAX)
1021: throw new IllegalStateException("block/fragment mixup");
1022:
1023: else if (fileOffset <= DOUBLE_INDIRECT_MAX) {
1024: long indirectAddr;
1025: indirectAddr = readLong(inode, inodeOffset
1026: + (DIRECT_BLOCKS + 1) * 8);
1027:
1028: if (indirectAddr == 0)
1029: throw new IllegalStateException(L.l("null block id"));
1030:
1031: int blockCount = (int) ((fileOffset - FRAGMENT_MAX) / BLOCK_SIZE);
1032:
1033: int offset = 8 * blockCount;
1034:
1035: long blockAddr = store.readFragmentLong(indirectAddr,
1036: offset);
1037:
1038: return blockAddr;
1039: } else
1040: throw new IllegalStateException(L.l(
1041: "size over {0}M not supported",
1042: (DOUBLE_INDIRECT_MAX / (1024 * 1024))));
1043: }
1044:
1045: /**
1046: * Writes the block id into the inode.
1047: */
1048: private static void writeBlockAddr(byte[] inode, int inodeOffset,
1049: Store store, StoreTransaction xa, long fileOffset,
1050: long blockAddr) throws IOException {
1051: if (fileOffset <= FRAGMENT_MAX)
1052: throw new IllegalStateException("block/fragment mixup");
1053:
1054: else if (fileOffset <= DOUBLE_INDIRECT_MAX) {
1055: long indAddr;
1056: indAddr = readLong(inode, inodeOffset + (DIRECT_BLOCKS + 1)
1057: * 8);
1058:
1059: if (indAddr == 0)
1060: throw new IllegalStateException(L.l("null block id"));
1061:
1062: int blockCount = (int) ((fileOffset - FRAGMENT_MAX) / BLOCK_SIZE);
1063:
1064: int dblBlockCount = blockCount / (BLOCK_SIZE / 8);
1065:
1066: long dblIndAddr = store.readFragmentLong(indAddr,
1067: dblBlockCount * 8);
1068:
1069: if (dblIndAddr == 0) {
1070: Block block = store.allocateBlock();
1071:
1072: dblIndAddr = Store.blockIdToAddress(block.getBlockId());
1073:
1074: block.free();
1075:
1076: store.writeBlockLong(xa, indAddr, dblBlockCount * 8,
1077: dblIndAddr);
1078: }
1079:
1080: int blockOffset = 8 * (blockCount % (BLOCK_SIZE / 8));
1081:
1082: store.writeFragmentLong(xa, dblIndAddr, blockOffset,
1083: blockAddr);
1084: } else
1085: throw new IllegalStateException(L.l(
1086: "size over {0}M not supported",
1087: (DOUBLE_INDIRECT_MAX / (1024 * 1024))));
1088: }
1089:
1090: /**
1091: * Reads the long.
1092: */
1093: public static long readLong(byte[] buffer, int offset) {
1094: return (((buffer[offset + 0] & 0xffL) << 56)
1095: + ((buffer[offset + 1] & 0xffL) << 48)
1096: + ((buffer[offset + 2] & 0xffL) << 40)
1097: + ((buffer[offset + 3] & 0xffL) << 32)
1098: + ((buffer[offset + 4] & 0xffL) << 24)
1099: + ((buffer[offset + 5] & 0xffL) << 16)
1100: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
1101: }
1102:
1103: /**
1104: * Writes the long.
1105: */
1106: public static void writeLong(byte[] buffer, int offset, long v) {
1107: buffer[offset + 0] = (byte) (v >> 56);
1108: buffer[offset + 1] = (byte) (v >> 48);
1109: buffer[offset + 2] = (byte) (v >> 40);
1110: buffer[offset + 3] = (byte) (v >> 32);
1111:
1112: buffer[offset + 4] = (byte) (v >> 24);
1113: buffer[offset + 5] = (byte) (v >> 16);
1114: buffer[offset + 6] = (byte) (v >> 8);
1115: buffer[offset + 7] = (byte) (v);
1116: }
1117:
1118: /**
1119: * Reads the short.
1120: */
1121: private static int readShort(byte[] buffer, int offset) {
1122: return (((buffer[offset + 0] & 0xff) << 8) + ((buffer[offset + 1] & 0xff)));
1123: }
1124:
1125: /**
1126: * Writes the short.
1127: */
1128: private static void writeShort(byte[] buffer, int offset, int v) {
1129: buffer[offset + 0] = (byte) (v >> 8);
1130: buffer[offset + 1] = (byte) v;
1131: }
1132:
1133: private static IllegalStateException stateError(String msg) {
1134: IllegalStateException e = new IllegalStateException(msg);
1135: e.fillInStackTrace();
1136: log.log(Level.WARNING, e.toString(), e);
1137: return e;
1138: }
1139: }
|