Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / store / raw / data / EncryptData.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/store/raw/data/EncryptData.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/store/raw/data/EncryptData.java
new file mode 100644 (file)
index 0000000..140381f
--- /dev/null
@@ -0,0 +1,442 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.store.raw.data.EncryptData\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.store.raw.data;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+import org.apache.derby.iapi.services.daemon.Serviceable;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.store.raw.data.RawContainerHandle;\r
+import org.apache.derby.iapi.store.raw.ContainerKey;\r
+import org.apache.derby.iapi.store.raw.LockingPolicy;\r
+import org.apache.derby.iapi.store.raw.Transaction;\r
+import org.apache.derby.iapi.store.raw.xact.RawTransaction;\r
+import org.apache.derby.iapi.store.raw.ContainerHandle;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+\r
+import org.apache.derby.io.StorageFactory;\r
+import org.apache.derby.io.StorageFile;\r
+import org.apache.derby.iapi.util.ReuseFactory;\r
+import java.security.AccessController;\r
+import java.security.PrivilegedAction;\r
+\r
+\r
+/**\r
+ * This class is used to encrypt all the containers in the data segment with a \r
+ * new encryption key when password/key is changed or when an existing database \r
+ * is reconfigured for encryption. \r
+ *  \r
+ * Encryption of existing data in the data segments is done by doing the \r
+ * following:\r
+ *  Find all the containers in data segment (seg0) and encrypt all of them\r
+ *  with the new  encryption key, the process for each container is:\r
+ *   1.Write a log record to indicate that the container is getting encrypted. \r
+ *   2.Read all the pages of the container through the page cache and\r
+ *       encrypt each page with new encryption key and then write to a \r
+ *       temporary file(n<cid>.dat) in the data segment itself.\r
+ *   3.        Rename the current container file (c<cid>.dat) to \r
+ *                                         another file (o<cid>.dat)\r
+ *   4.        Rename the new encrypted version of the file (n<cid).dat) to be \r
+ *                                    the current container file (c<cid>.dat).\r
+ *   5.        All the old version of  the container (o<cid>.dat) files are removed\r
+ *      after a successful checkpoint with a new key or on a rollback.\r
+ *   \r
+ */\r
+\r
+public class EncryptData implements PrivilegedAction {\r
+\r
+    private BaseDataFileFactory dataFactory;\r
+    private StorageFactory storageFactory;\r
+    private StorageFile[] oldFiles;\r
+    private int noOldFiles = 0; \r
+\r
+\r
+    /* privileged actions */\r
+    private static final int STORAGE_FILE_EXISTS_ACTION = 1;\r
+    private static final int STORAGE_FILE_DELETE_ACTION = 2;\r
+    private static final int STORAGE_FILE_RENAME_ACTION = 3;\r
+    private int actionCode;\r
+    private StorageFile actionStorageFile;\r
+    private StorageFile actionDestStorageFile;\r
+\r
+       public EncryptData(BaseDataFileFactory dataFactory) {\r
+               this.dataFactory = dataFactory;\r
+        this.storageFactory = dataFactory.getStorageFactory();\r
+       }\r
+\r
+\r
+    /*\r
+     * Find all the all the containers stored in the data directory and \r
+     * encrypt them.\r
+     * @param t the transaction that is used to configure the database \r
+     *          with new encryption properties.\r
+     * @exception StandardException Standard Derby error policy\r
+        */\r
+       public void encryptAllContainers(RawTransaction t) \r
+        throws StandardException {\r
+\r
+        /*\r
+                * List of containers that needs to be encrypted are identified by \r
+                * simply reading the list of files in seg0. \r
+                */\r
+\r
+               String[] files = dataFactory.getContainerNames();\r
+               if (files != null) {\r
+            oldFiles = new StorageFile[files.length];\r
+            noOldFiles = 0;\r
+                       long segmentId = 0;\r
+\r
+            // loop through all the files in seg0 and \r
+            // encrypt all valid containers.\r
+                       for (int f = files.length-1; f >= 0 ; f--) {\r
+                               long containerId;\r
+                               try     {\r
+                                       containerId = \r
+                                               Long.parseLong(files[f].substring(1, \r
+                                       (files[f].length() -4)), 16);\r
+                               }\r
+                               catch (Throwable th)\r
+                               {\r
+                    // ignore errors from parse, it just means \r
+                    // that someone put a file in seg0 that we \r
+                    // didn't expect.  Continue with the next one.\r
+                                       continue;\r
+                               }\r
+\r
+                               ContainerKey ckey = new ContainerKey(segmentId, \r
+                                                     containerId);\r
+                oldFiles[noOldFiles++] = encryptContainer(t, ckey);\r
+                       }\r
+\r
+            // Old versions of the container files will\r
+            // be removed after the (re)encryption of database\r
+            // is completed. \r
+               } else\r
+               {\r
+                       if (SanityManager.DEBUG) \r
+                               SanityManager.THROWASSERT("encryption process is unable to" +\r
+                                          "read container names in seg0");\r
+               }\r
+\r
+    }\r
+\r
+\r
+       /** Encrypt a container.\r
+     * @param t    the transaction that is used to configure the database \r
+     *             with new encryption properties.\r
+     * @param ckey the key of the container that is being encrypted.\r
+     * @return     file handle to the old copy  of the container.\r
+     * @exception StandardException Standard Derby error policy\r
+     */\r
+       private StorageFile encryptContainer(RawTransaction  t, \r
+                                         ContainerKey    ckey)\r
+        throws StandardException\r
+       {\r
+\r
+        LockingPolicy cl = \r
+            t.newLockingPolicy(\r
+                               LockingPolicy.MODE_CONTAINER,\r
+                               TransactionController.ISOLATION_SERIALIZABLE, \r
+                               true);\r
+               \r
+        if (SanityManager.DEBUG )\r
+            SanityManager.ASSERT(cl != null);\r
+\r
+        RawContainerHandle containerHdl = (RawContainerHandle)\r
+            t.openContainer(ckey, cl, ContainerHandle.MODE_FORUPDATE);\r
+\r
+        if (SanityManager.DEBUG )\r
+            SanityManager.ASSERT(containerHdl != null);\r
+\r
+        EncryptContainerOperation lop = \r
+            new EncryptContainerOperation(containerHdl);\r
+        t.logAndDo(lop);\r
+        \r
+        // flush the log to reduce the window between where\r
+        // the encrypted container is created & synced and the \r
+        // log record for it makes it to disk. if we fail during \r
+        // encryption of the container, log record will make sure \r
+        // container is restored to the original state and \r
+        // any temporary files are cleaned up. \r
+        dataFactory.flush(t.getLastLogInstant());\r
+\r
+        // encrypt the container.\r
+        String newFilePath = getFilePath(ckey, false);\r
+        StorageFile newFile = storageFactory.newStorageFile(newFilePath);\r
+        containerHdl.encryptContainer(newFilePath);\r
+        containerHdl.close();\r
+\r
+                    \r
+        /*\r
+         * Replace the current container file with the new container file after\r
+         * keeping a copy of the current container file, it will be removed on \r
+         * after a checkpoint with new key or on a rollback this copy will be \r
+         * replace the container file to bring the database back to the \r
+         * state before encryption process started.  \r
+         */\r
+\r
+        // discard pages in the cache related to this container. \r
+        if (!dataFactory.getPageCache().discard(ckey)) {\r
+            if (SanityManager.DEBUG )\r
+                SanityManager.THROWASSERT("unable to discard pages releated to " + \r
+                                          "container " + ckey  + \r
+                                          " from the page cache");\r
+        }\r
+\r
+\r
+        // get rid of the container entry from conatainer cache\r
+        if (!dataFactory.getContainerCache().discard(ckey)) {\r
+            if (SanityManager.DEBUG )\r
+                SanityManager.THROWASSERT("unable to discard a container " + \r
+                                          ckey + " from the container cache");\r
+        }\r
+\r
+        StorageFile currentFile =  dataFactory.getContainerPath(ckey , false);\r
+        StorageFile oldFile = getFile(ckey, true);\r
+\r
+        if (!privRename(currentFile, oldFile)) {\r
+                throw StandardException.\r
+                    newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,\r
+                                 currentFile, oldFile);\r
+            }\r
+\r
+        // now replace current container file with the new file. \r
+        if (!privRename(newFile, currentFile)) {\r
+            throw StandardException.\r
+                newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,\r
+                             newFile, currentFile);\r
+                \r
+        }\r
+\r
+        return oldFile ;\r
+    }\r
+\r
+    \r
+    /**\r
+     * Get file handle to a container file that is used to keep \r
+     * temporary versions of the container file.  \r
+     */\r
+    private StorageFile getFile(ContainerKey containerId, boolean old) {\r
+        String path = getFilePath(containerId, old);\r
+        return storageFactory.newStorageFile(getFilePath(containerId, \r
+                                                         old));\r
+    }\r
+\r
+    /**\r
+     * Get path to a container file that is used to keep temporary versions of\r
+     * the container file.  \r
+     */\r
+    private String getFilePath(ContainerKey containerId, boolean old) {\r
+        StringBuffer sb = new StringBuffer("seg");\r
+        sb.append(containerId.getSegmentId());\r
+        sb.append(storageFactory.getSeparator());\r
+        sb.append(old ? 'o' : 'n');\r
+        sb.append(Long.toHexString(containerId.getContainerId()));\r
+        sb.append(".dat");\r
+        return sb.toString();\r
+    }\r
+\r
+    private boolean isOldContainerFile(String fileName) \r
+    {\r
+        // all old versions of the conatainer files\r
+        // start with prefix "o" and ends with ".dat"\r
+        if (fileName.startsWith("o") && fileName.endsWith(".dat"))\r
+            return true;\r
+        else\r
+            return false;\r
+    }\r
+\r
+    private StorageFile getFile(String ctrFileName) \r
+    {\r
+        long segmentId = 0;\r
+        StringBuffer sb = new StringBuffer("seg");\r
+        sb.append(segmentId);\r
+        sb.append(storageFactory.getSeparator());\r
+        sb.append(ctrFileName);\r
+        return storageFactory.newStorageFile(sb.toString());\r
+    }\r
+\r
+    /* Restore the contaier to the state it was before \r
+     * it was encrypted with new encryption key. This function is \r
+     * called during undo of the EncryptContainerOperation log record \r
+     * incase of a error/crash before database was successfuly configured with\r
+     * new encryption properties.\r
+     * @param ckey the key of the container that needs to be restored.\r
+     * @exception StandardException Standard Derby error policy\r
+     */\r
+    void restoreContainer(ContainerKey containerId) \r
+        throws StandardException \r
+    {\r
+\r
+        // get rid of the container entry from conatainer cache,\r
+        // this will make sure there are no file opens on the current \r
+        // container file. \r
+        \r
+        if (!dataFactory.getContainerCache().discard(containerId)) {\r
+            if (SanityManager.DEBUG )\r
+                SanityManager.THROWASSERT(\r
+                  "unable to discard  container from cache:" + \r
+                  containerId);\r
+        }\r
+\r
+        StorageFile currentFile = dataFactory.getContainerPath(containerId, \r
+                                                               false);\r
+        StorageFile oldFile = getFile(containerId, true);\r
+        StorageFile newFile = getFile(containerId, false);\r
+        \r
+        // if backup of the original container file exists, replace the \r
+        // container with the backup copy.\r
+        if (privExists(oldFile)) {\r
+            if (privExists(currentFile)) {\r
+                // rename the current container file to be the new file.\r
+                if (!privRename(currentFile, newFile)) {\r
+                    throw StandardException.\r
+                        newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,\r
+                                     currentFile, newFile);\r
+                }\r
+            }\r
+\r
+            if (!privRename(oldFile, currentFile)) {\r
+                throw StandardException.\r
+                    newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,\r
+                                 oldFile, currentFile);\r
+            }\r
+        }\r
+\r
+        // if the new copy of the container file exists, remove it.\r
+        if (privExists(newFile)) {\r
+\r
+            if (!privDelete(newFile))\r
+                throw StandardException.newException(\r
+                                                 SQLState.UNABLE_TO_DELETE_FILE, \r
+                                                 newFile);\r
+        }\r
+    }\r
+\r
+\r
+    /*\r
+     * Remove all the old version (encrypted with old key or \r
+     * un-encrypted) of the containers stored in the data directory .\r
+     *\r
+     * @param inRecovery  <code> true </code>, if cleanup is \r
+     *                    happening during recovery.\r
+     * @exception StandardException Standard Derby Error Policy\r
+     */\r
+    public void removeOldVersionOfContainers(boolean inRecovery) \r
+        throws StandardException\r
+    {\r
+        \r
+        if (inRecovery) \r
+        {\r
+            // find the old version of the container files\r
+            // and delete them\r
+            String[] files = dataFactory.getContainerNames();\r
+            if (files != null) \r
+            {\r
+                // loop through all the files in seg0 and \r
+                // delete all old copies of the containers.\r
+                for (int i = files.length-1; i >= 0 ; i--) \r
+                {\r
+                    // if it is a old version of the container file\r
+                    // delete it. \r
+                    if (isOldContainerFile(files[i]))\r
+                    {\r
+                        StorageFile oldFile = getFile(files[i]);\r
+                        if (!privDelete(oldFile)) \r
+                        {\r
+                            throw StandardException.newException(\r
+                                          SQLState.FILE_CANNOT_REMOVE_FILE,\r
+                                          oldFile);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }else \r
+        {\r
+            // delete all the old version of the containers. \r
+            for (int i = 0 ; i < noOldFiles ; i++) \r
+            {\r
+                if (!privDelete(oldFiles[i])) \r
+                {\r
+                    throw StandardException.newException(\r
+                                   SQLState.FILE_CANNOT_REMOVE_FILE, \r
+                                   oldFiles[i]);\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+    \r
+    private synchronized boolean privExists(StorageFile file)\r
+    {\r
+        actionCode = STORAGE_FILE_EXISTS_ACTION;\r
+        actionStorageFile = file;\r
+        Object ret = AccessController.doPrivileged(this);\r
+        actionStorageFile = null;\r
+        return ((Boolean) ret).booleanValue();\r
+\r
+    }\r
+\r
+    \r
+    private synchronized boolean privDelete(StorageFile file)\r
+    {\r
+        actionCode = STORAGE_FILE_DELETE_ACTION;\r
+        actionStorageFile = file;\r
+        Object ret = AccessController.doPrivileged(this);\r
+        actionStorageFile = null;\r
+        return ((Boolean) ret).booleanValue();\r
+        \r
+    }\r
+\r
+    private synchronized boolean privRename(StorageFile fromFile, \r
+                                            StorageFile destFile)\r
+    {\r
+        actionCode = STORAGE_FILE_RENAME_ACTION;\r
+        actionStorageFile = fromFile;\r
+        actionDestStorageFile = destFile;\r
+        Object ret = AccessController.doPrivileged(this);\r
+        actionStorageFile = null;\r
+        actionDestStorageFile = null;\r
+        return ((Boolean) ret).booleanValue();\r
+\r
+    }\r
+\r
+\r
+\r
+    // PrivilegedAction method\r
+    public Object run() \r
+    {\r
+        switch(actionCode)\r
+        {\r
+        case STORAGE_FILE_EXISTS_ACTION:\r
+            return ReuseFactory.getBoolean(actionStorageFile.exists());\r
+        case STORAGE_FILE_DELETE_ACTION:\r
+            return ReuseFactory.getBoolean(actionStorageFile.delete());\r
+        case STORAGE_FILE_RENAME_ACTION:\r
+            return ReuseFactory.getBoolean(\r
+                       actionStorageFile.renameTo(actionDestStorageFile));\r
+        }\r
+\r
+        return null;\r
+    }\r
+}\r