Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / jdbc / EncryptedLOBFile.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/jdbc/EncryptedLOBFile.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/jdbc/EncryptedLOBFile.java
new file mode 100644 (file)
index 0000000..0487644
--- /dev/null
@@ -0,0 +1,360 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.jdbc.EncryptedLOBFile\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.jdbc;\r
+\r
+import java.io.EOFException;\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.store.raw.data.DataFactory;\r
+import org.apache.derby.io.StorageFile;\r
+\r
+/**\r
+ * This class is a wrapper class on top of StorageRandomAccess to provide common\r
+ * methods to write in encrypted file.\r
+ * This class is NOT thread safe. The user class should take care\r
+ * of synchronization if being used in multi threaded environment.\r
+ */\r
+class EncryptedLOBFile extends LOBFile {\r
+    /** Block size for encryption. */\r
+    private final int blockSize;\r
+    /** Leftover bytes. Stored in memory until they fill one block .*/\r
+    private final byte [] tail;\r
+    /** Number of actual bytes in tail array. */\r
+    private int tailSize;\r
+    /** Current file position. */\r
+    private long currentPos;\r
+    /** Factory object used for encryption and decryption. */\r
+    private final DataFactory df;\r
+\r
+    /**\r
+     * Constructs the EncryptedLOBFile object with encryption support.\r
+     *\r
+     * @param lobFile StorageFile Object for which file will be created\r
+     * @param df data factory for encryption and decription\r
+     * @throws FileNotFoundException if the file exists but is a directory or\r
+     * cannot be opened\r
+     */\r
+    EncryptedLOBFile(StorageFile lobFile, DataFactory df)\r
+                                                throws FileNotFoundException {\r
+        super(lobFile);\r
+        this.df = df;\r
+        blockSize = df.getEncryptionBlockSize();\r
+        tail = new byte [blockSize];\r
+        tailSize = 0;\r
+    }\r
+\r
+    /**\r
+     * Find the blocks containing the data we are interested in.\r
+     *\r
+     * @param pos first position we are interested in\r
+     * @param len number of bytes of interest\r
+     * @return byte array containing all the blocks of data the specified\r
+     * region spans over\r
+     */\r
+    private byte [] getBlocks (long pos, int len)\r
+                                        throws IOException, StandardException {\r
+        if (len < 0)\r
+            throw new IndexOutOfBoundsException (\r
+                    MessageService.getTextMessage (\r
+                        SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer (len)));\r
+        //starting position of the 1st block\r
+        long startPos = pos - pos % blockSize;\r
+        //end position of last block\r
+        long endPos = (pos + len + blockSize - 1) / blockSize * blockSize;\r
+\r
+        byte [] data = new byte [(int) (endPos - startPos)];\r
+        super.seek (startPos);\r
+        super.read (data, 0, data.length);\r
+        return data;\r
+    }\r
+\r
+    /**\r
+     * Returns file length.\r
+     * @return file length\r
+     * @throws IOException if an I/O error occurs\r
+     */\r
+    long length() throws IOException {\r
+        return super.length() + tailSize;\r
+    }\r
+\r
+    /**\r
+     * Returns the currrent position in the file.\r
+     * @return current position of file pointer\r
+     */\r
+    long getFilePointer() {\r
+        return currentPos;\r
+    }\r
+\r
+    /**\r
+     * Sets the current file pointer to specific location.\r
+     * @param pos new position\r
+     * @throws IOException\r
+     */\r
+    void seek (long pos) throws IOException {\r
+        long fileLength = super.length();\r
+        if (pos > fileLength + tailSize) {\r
+            //this should never happen\r
+            //this exception will mean internal error most\r
+            //probably in LOBStreamControl\r
+            throw new IllegalArgumentException ("Internal Error");\r
+        }\r
+        if (pos < fileLength) {\r
+            super.seek (pos);\r
+        }\r
+        currentPos = pos;\r
+    }\r
+\r
+    /**\r
+     * Writes one byte into the file.\r
+     * @param b byte value\r
+     * @throws IOException if disk operation fails\r
+     * @throws StandardException if error occured during encryption/decryption\r
+     */\r
+    void write (int b) throws IOException, StandardException {\r
+        long length = super.length();\r
+        if (currentPos >= length) {\r
+            //current position is in memory\r
+            int pos = (int) (currentPos - length);\r
+            tail [pos] = (byte) b;\r
+            if (pos >= tailSize) {\r
+                tailSize = pos + 1;\r
+            }\r
+            if (tailSize == blockSize) {\r
+                //we have enough data to fill one block encrypt and write\r
+                //in file\r
+                byte [] cypherText = new byte [blockSize];\r
+                df.encrypt (tail, 0, tailSize, cypherText, 0, false);\r
+                super.seek (length);\r
+                super.write (cypherText);\r
+                tailSize = 0;\r
+            }\r
+        }\r
+        else {\r
+            //write position is in the middle of the file\r
+            //get the complete block in which the destination byte falls into\r
+            byte [] cypherText = getBlocks (currentPos, 1);\r
+            byte [] clearText = new byte [blockSize];\r
+            //decrypt the block before updating\r
+            df.decrypt(cypherText, 0, blockSize, clearText, 0);\r
+            clearText [(int) (currentPos%blockSize)] = (byte) b;\r
+            //encrypt and write back\r
+            df.encrypt (clearText, 0, blockSize, cypherText, 0, false);\r
+            super.seek (currentPos - currentPos % blockSize);\r
+            super.write (cypherText);\r
+        }\r
+        currentPos++;\r
+    }\r
+\r
+    /**\r
+     * Writes length number of bytes from buffer starting from off position.\r
+     * @param b byte array containing bytes to be written\r
+     * @param off starting offset of the byte array from where the\r
+     * data should be written to the file\r
+     * @param len number of bytes to be written\r
+     * @throws IOException if disk operation fails\r
+     * @throws StandardException if error occured during encryption/decryption\r
+     */\r
+    void write(byte[] b, int off, int len)\r
+                                    throws IOException, StandardException {\r
+        long fileLength = super.length();\r
+        if (currentPos < fileLength) {\r
+            //starting position for write is in file\r
+            //find out if we need to update memory\r
+            int overFlow = (int) Math.max(0L, currentPos + len - fileLength);\r
+            long oldPos = currentPos;\r
+            //get the block containing bytes we are going to overwrite\r
+            byte [] cypherText = getBlocks (currentPos, len - overFlow);\r
+            byte [] clearText = new byte [cypherText.length];\r
+            //decrypt the data before updating\r
+            for (int i = 0; i < cypherText.length / blockSize; i++)\r
+                df.decrypt (cypherText, i * blockSize, blockSize, clearText,\r
+                                                                i * blockSize);\r
+            //update the data\r
+            System.arraycopy (b, off, clearText, (int) (currentPos%blockSize),\r
+                len - overFlow);\r
+            //encrypt and write back\r
+            for (int i = 0; i < cypherText.length / blockSize; i++)\r
+                df.encrypt (clearText, i * blockSize, blockSize,\r
+                                        cypherText, i * blockSize, false);\r
+            super.seek (oldPos - oldPos % blockSize);\r
+            super.write (cypherText);\r
+            currentPos = oldPos + cypherText.length;\r
+            //nothing to keep in memory.\r
+            if (overFlow == 0)\r
+                return;\r
+            //adjust the value to perform rest of the writes in tail buffer\r
+            off = off + len - overFlow;\r
+            len = overFlow;\r
+            //write rest of the data in memory\r
+            currentPos = fileLength;\r
+        }\r
+        //starting position in array\r
+        int pos = (int) (currentPos - fileLength);\r
+        int finalPos = pos + len;\r
+        if (finalPos < blockSize) {\r
+            //updated size won't be enough to perform encryption\r
+            System.arraycopy (b, off, tail, pos, len);\r
+            tailSize = Math.max(tailSize, pos + len);\r
+            currentPos += len;\r
+            return;\r
+        }\r
+        //number of bytes which can be encrypted\r
+        int encLength = finalPos - finalPos % blockSize;\r
+        int leftOver = finalPos % blockSize;\r
+        byte [] clearText = new byte [encLength];\r
+        //create array to encrypt\r
+        //copy the bytes from tail which won't be overwritten\r
+        System.arraycopy (tail, 0, clearText, 0, pos);\r
+        //copy remaining data into array\r
+        System.arraycopy (b, off, clearText, pos, encLength - pos);\r
+        byte [] cypherText = new byte [clearText.length];\r
+        //encrypt and write\r
+        for (int offset = 0; offset < cypherText.length ; offset += blockSize)\r
+            df.encrypt (clearText, offset, blockSize, cypherText,\r
+                                                        offset, false);\r
+        super.seek (fileLength);\r
+        super.write (cypherText);\r
+        //copy rest of it in tail\r
+        System.arraycopy (b, off + len - leftOver, tail, 0, leftOver);\r
+        tailSize = leftOver;\r
+        currentPos = tailSize + fileLength + cypherText.length;\r
+    }\r
+\r
+    /**\r
+     * Write the buffer into file at current position. It overwrites the\r
+     * data if current position is in the middle of the file and appends into\r
+     * the file if the total length exceeds the file size.\r
+     * @param b byte array to be written\r
+     * @throws IOException if disk operation fails\r
+     * @throws StandardException if error occured during encryption/decryption\r
+     */\r
+    void write(byte[] b) throws IOException, StandardException {\r
+        write (b, 0, b.length);\r
+    }\r
+\r
+    /**\r
+     * closes the file.\r
+     * @throws IOException\r
+     */\r
+    void close() throws IOException {\r
+        super.close();\r
+    }\r
+\r
+    /**\r
+     * Reads one byte from file.\r
+     * @return byte\r
+     * @throws IOException if disk operation fails\r
+     * @throws StandardException if error occured during encryption/decryption\r
+     */\r
+    int readByte() throws IOException, StandardException {\r
+        long fileLength = super.length();\r
+        if (currentPos >= fileLength + tailSize)\r
+            throw new EOFException ();\r
+        if (currentPos >= fileLength)\r
+            return tail [(int) (currentPos++ - fileLength)] & 0xff;\r
+        //get the block containing the byte we are interested in\r
+        byte cypherText [] = getBlocks (currentPos, 1);\r
+        byte [] clearText = new byte [cypherText.length];\r
+        df.decrypt (cypherText, 0, cypherText.length, clearText, 0);\r
+        return clearText [(int) (currentPos++ % blockSize)] & 0xff;\r
+    }\r
+\r
+    /**\r
+     * Reads len or remaining bytes in the file (whichever is lower) bytes\r
+     * into buff starting from off position of the buffer.\r
+     * @param buff byte array to fill read bytes\r
+     * @param off offset of buff where the byte will be written\r
+     * @param len number of bytes to be read\r
+     * @return number of bytes read\r
+     * @throws IOException if disk operation fails\r
+     * @throws StandardException if error occured during encryption/decryption\r
+     */\r
+    int read(byte[] buff, int off, int len)\r
+                                        throws IOException, StandardException {\r
+        long fileLength = super.length();\r
+        if (currentPos < fileLength) {\r
+            //starting position is in file\r
+            //find number of bytes spilling out of file\r
+            int overFlow = (int) Math.max(0L, currentPos + len - fileLength);\r
+            //get all the blocks\r
+            byte [] cypherText = getBlocks (currentPos, len - overFlow);\r
+            byte [] tmpByte = new byte [cypherText.length];\r
+            //decrypt\r
+            for (int offset = 0; offset < cypherText.length; offset += blockSize) {\r
+                df.decrypt (cypherText, offset, blockSize, tmpByte,\r
+                                                                offset);\r
+            }\r
+            //copy the bytes we are interested in\r
+            System.arraycopy (tmpByte, (int) (currentPos%blockSize), buff,\r
+                                                        off, len - overFlow);\r
+            if (overFlow == 0) {\r
+                currentPos += len;\r
+                return len;\r
+            }\r
+            //find out total number of bytes we can read\r
+            int newLen = Math.min(overFlow, tailSize);\r
+            //fill the buffer from tail\r
+            System.arraycopy (tail, 0, buff, off + len - overFlow, newLen);\r
+            currentPos += len - overFlow + newLen;\r
+            return len - overFlow + newLen;\r
+        }\r
+        int newLen = (int) Math.min (\r
+            tailSize - currentPos + fileLength, len);\r
+        if (newLen == 0 && len != 0)\r
+            return -1;\r
+\r
+        System.arraycopy (tail, (int) (currentPos - fileLength),\r
+                            buff, off, newLen);\r
+        currentPos += newLen;\r
+        return newLen;\r
+    }\r
+\r
+    /**\r
+     * Sets the file length to a given size. If the new size is smaller than the\r
+     * file length the file is truncated.\r
+     *\r
+     * @param size new  file size. Must be lower than file length.\r
+     * @throws IOException if file i/o fails\r
+     * @throws StandardException if error occured during encryption/decryption\r
+     */\r
+    void setLength(long size) throws IOException, StandardException {\r
+        long fileLength = super.length();\r
+        if (size > fileLength + tailSize) {\r
+            //this should never happen\r
+            //this exception will mean internal error most\r
+            //probably in LOBStreamControl\r
+            throw new IllegalArgumentException ("Internal Error");\r
+        }\r
+        if (size < fileLength) {\r
+            byte [] block = getBlocks (size, 1);\r
+            super.setLength (size - size % blockSize);\r
+            df.decrypt (block, 0, blockSize, tail, 0);\r
+            tailSize = (int) (size % blockSize);\r
+        }\r
+        else {\r
+            tailSize = (int) (size - fileLength);\r
+        }\r
+    }\r
+}\r