KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > store > raw > data > EncryptData


1 /*
2
3    Derby - Class org.apache.derby.impl.store.raw.data.EncryptData
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.store.raw.data;
23 import org.apache.derby.iapi.reference.SQLState;
24 import org.apache.derby.iapi.services.context.ContextManager;
25 import org.apache.derby.iapi.services.daemon.Serviceable;
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27 import org.apache.derby.iapi.error.StandardException;
28 import org.apache.derby.iapi.store.raw.data.RawContainerHandle;
29 import org.apache.derby.iapi.store.raw.ContainerKey;
30 import org.apache.derby.iapi.store.raw.LockingPolicy;
31 import org.apache.derby.iapi.store.raw.Transaction;
32 import org.apache.derby.iapi.store.raw.xact.RawTransaction;
33 import org.apache.derby.iapi.store.raw.ContainerHandle;
34 import org.apache.derby.iapi.store.access.TransactionController;
35
36 import org.apache.derby.io.StorageFactory;
37 import org.apache.derby.io.StorageFile;
38 import org.apache.derby.iapi.util.ReuseFactory;
39 import java.security.AccessController JavaDoc;
40 import java.security.PrivilegedAction JavaDoc;
41
42
43 /**
44  * This class is used to encrypt all the containers in the data segment with a
45  * new encryption key when password/key is changed or when an existing database
46  * is reconfigured for encryption.
47  *
48  * Encryption of existing data in the data segments is done by doing the
49  * following:
50  * Find all the containers in data segment (seg0) and encrypt all of them
51  * with the new encryption key, the process for each container is:
52  * 1.Write a log record to indicate that the container is getting encrypted.
53  * 2.Read all the pages of the container through the page cache and
54  * encrypt each page with new encryption key and then write to a
55  * temporary file(n<cid>.dat) in the data segment itself.
56  * 3. Rename the current container file (c<cid>.dat) to
57  * another file (o<cid>.dat)
58  * 4. Rename the new encrypted version of the file (n<cid).dat) to be
59  * the current container file (c<cid>.dat).
60  * 5. All the old version of the container (o<cid>.dat) files are removed
61  * after a successful checkpoint with a new key or on a rollback.
62  *
63  * @author Suresh Thalamati
64  */

65
66 public class EncryptData implements PrivilegedAction JavaDoc {
67
68     private BaseDataFileFactory dataFactory;
69     private StorageFactory storageFactory;
70     private StorageFile[] oldFiles;
71     private int noOldFiles = 0;
72
73
74     /* privileged actions */
75     private static final int STORAGE_FILE_EXISTS_ACTION = 1;
76     private static final int STORAGE_FILE_DELETE_ACTION = 2;
77     private static final int STORAGE_FILE_RENAME_ACTION = 3;
78     private int actionCode;
79     private StorageFile actionStorageFile;
80     private StorageFile actionDestStorageFile;
81
82     public EncryptData(BaseDataFileFactory dataFactory) {
83         this.dataFactory = dataFactory;
84         this.storageFactory = dataFactory.getStorageFactory();
85     }
86
87
88     /*
89      * Find all the all the containers stored in the data directory and
90      * encrypt them.
91      * @param t the transaction that is used to configure the database
92      * with new encryption properties.
93      * @exception StandardException Standard Derby error policy
94      */

95     public void encryptAllContainers(RawTransaction t)
96         throws StandardException {
97
98         /*
99          * List of containers that needs to be encrypted are identified by
100          * simply reading the list of files in seg0.
101          */

102
103         String JavaDoc[] files = dataFactory.getContainerNames();
104         if (files != null) {
105             oldFiles = new StorageFile[files.length];
106             noOldFiles = 0;
107             long segmentId = 0;
108
109             // loop through all the files in seg0 and
110
// encrypt all valid containers.
111
for (int f = files.length-1; f >= 0 ; f--) {
112                 long containerId;
113                 try {
114                     containerId =
115                         Long.parseLong(files[f].substring(1,
116                                        (files[f].length() -4)), 16);
117                 }
118                 catch (Throwable JavaDoc th)
119                 {
120                     // ignore errors from parse, it just means
121
// that someone put a file in seg0 that we
122
// didn't expect. Continue with the next one.
123
continue;
124                 }
125
126                 ContainerKey ckey = new ContainerKey(segmentId,
127                                                      containerId);
128                 oldFiles[noOldFiles++] = encryptContainer(t, ckey);
129             }
130
131             // Old versions of the container files will
132
// be removed after the (re)encryption of database
133
// is completed.
134
} else
135         {
136             if (SanityManager.DEBUG)
137                 SanityManager.THROWASSERT("encryption process is unable to" +
138                                           "read container names in seg0");
139         }
140
141     }
142
143
144     /** Encrypt a container.
145      * @param t the transaction that is used to configure the database
146      * with new encryption properties.
147      * @param ckey the key of the container that is being encrypted.
148      * @return file handle to the old copy of the container.
149      * @exception StandardException Standard Derby error policy
150      */

151     private StorageFile encryptContainer(RawTransaction t,
152                                          ContainerKey ckey)
153         throws StandardException
154     {
155
156         LockingPolicy cl =
157             t.newLockingPolicy(
158                                LockingPolicy.MODE_CONTAINER,
159                                TransactionController.ISOLATION_SERIALIZABLE,
160                                true);
161         
162         if (SanityManager.DEBUG )
163             SanityManager.ASSERT(cl != null);
164
165         RawContainerHandle containerHdl = (RawContainerHandle)
166             t.openContainer(ckey, cl, ContainerHandle.MODE_FORUPDATE);
167
168         if (SanityManager.DEBUG )
169             SanityManager.ASSERT(containerHdl != null);
170
171         EncryptContainerOperation lop =
172             new EncryptContainerOperation(containerHdl);
173         t.logAndDo(lop);
174         
175         // flush the log to reduce the window between where
176
// the encrypted container is created & synced and the
177
// log record for it makes it to disk. if we fail during
178
// encryption of the container, log record will make sure
179
// container is restored to the original state and
180
// any temporary files are cleaned up.
181
dataFactory.flush(t.getLastLogInstant());
182
183         // encrypt the container.
184
String JavaDoc newFilePath = getFilePath(ckey, false);
185         StorageFile newFile = storageFactory.newStorageFile(newFilePath);
186         containerHdl.encryptContainer(newFilePath);
187         containerHdl.close();
188
189                     
190         /*
191          * Replace the current container file with the new container file after
192          * keeping a copy of the current container file, it will be removed on
193          * after a checkpoint with new key or on a rollback this copy will be
194          * replace the container file to bring the database back to the
195          * state before encryption process started.
196          */

197
198         // discard pages in the cache related to this container.
199
if (!dataFactory.getPageCache().discard(ckey)) {
200             if (SanityManager.DEBUG )
201                 SanityManager.THROWASSERT("unable to discard pages releated to " +
202                                           "container " + ckey +
203                                           " from the page cache");
204         }
205
206
207         // get rid of the container entry from conatainer cache
208
if (!dataFactory.getContainerCache().discard(ckey)) {
209             if (SanityManager.DEBUG )
210                 SanityManager.THROWASSERT("unable to discard a container " +
211                                           ckey + " from the container cache");
212         }
213
214         StorageFile currentFile = dataFactory.getContainerPath(ckey , false);
215         StorageFile oldFile = getFile(ckey, true);
216
217         if (!privRename(currentFile, oldFile)) {
218                 throw StandardException.
219                     newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,
220                                  currentFile, oldFile);
221             }
222
223         // now replace current container file with the new file.
224
if (!privRename(newFile, currentFile)) {
225             throw StandardException.
226                 newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,
227                              newFile, currentFile);
228                 
229         }
230
231         return oldFile ;
232     }
233
234     
235     /**
236      * Get file handle to a container file that is used to keep
237      * temporary versions of the container file.
238      */

239     private StorageFile getFile(ContainerKey containerId, boolean old) {
240         String JavaDoc path = getFilePath(containerId, old);
241         return storageFactory.newStorageFile(getFilePath(containerId,
242                                                          old));
243     }
244
245     /**
246      * Get path to a container file that is used to keep temporary versions of
247      * the container file.
248      */

249     private String JavaDoc getFilePath(ContainerKey containerId, boolean old) {
250         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("seg");
251         sb.append(containerId.getSegmentId());
252         sb.append(storageFactory.getSeparator());
253         sb.append(old ? 'o' : 'n');
254         sb.append(Long.toHexString(containerId.getContainerId()));
255         sb.append(".dat");
256         return sb.toString();
257     }
258
259     private boolean isOldContainerFile(String JavaDoc fileName)
260     {
261         // all old versions of the conatainer files
262
// start with prefix "o" and ends with ".dat"
263
if (fileName.startsWith("o") && fileName.endsWith(".dat"))
264             return true;
265         else
266             return false;
267     }
268
269     private StorageFile getFile(String JavaDoc ctrFileName)
270     {
271         long segmentId = 0;
272         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("seg");
273         sb.append(segmentId);
274         sb.append(storageFactory.getSeparator());
275         sb.append(ctrFileName);
276         return storageFactory.newStorageFile(sb.toString());
277     }
278
279     /* Restore the contaier to the state it was before
280      * it was encrypted with new encryption key. This function is
281      * called during undo of the EncryptContainerOperation log record
282      * incase of a error/crash before database was successfuly configured with
283      * new encryption properties.
284      * @param ckey the key of the container that needs to be restored.
285      * @exception StandardException Standard Derby error policy
286      */

287     void restoreContainer(ContainerKey containerId)
288         throws StandardException
289     {
290
291         // get rid of the container entry from conatainer cache,
292
// this will make sure there are no file opens on the current
293
// container file.
294

295         if (!dataFactory.getContainerCache().discard(containerId)) {
296             if (SanityManager.DEBUG )
297                 SanityManager.THROWASSERT(
298                   "unable to discard container from cache:" +
299                   containerId);
300         }
301
302         StorageFile currentFile = dataFactory.getContainerPath(containerId,
303                                                                false);
304         StorageFile oldFile = getFile(containerId, true);
305         StorageFile newFile = getFile(containerId, false);
306         
307         // if backup of the original container file exists, replace the
308
// container with the backup copy.
309
if (privExists(oldFile)) {
310             if (privExists(currentFile)) {
311                 // rename the current container file to be the new file.
312
if (!privRename(currentFile, newFile)) {
313                     throw StandardException.
314                         newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,
315                                      currentFile, newFile);
316                 }
317             }
318
319             if (!privRename(oldFile, currentFile)) {
320                 throw StandardException.
321                     newException(SQLState.RAWSTORE_ERROR_RENAMING_FILE,
322                                  oldFile, currentFile);
323             }
324         }
325
326         // if the new copy of the container file exists, remove it.
327
if (privExists(newFile)) {
328
329             if (!privDelete(newFile))
330                 throw StandardException.newException(
331                                                  SQLState.UNABLE_TO_DELETE_FILE,
332                                                  newFile);
333         }
334     }
335
336
337     /*
338      * Remove all the old version (encrypted with old key or
339      * un-encrypted) of the containers stored in the data directory .
340      *
341      * @param inRecovery <code> true </code>, if cleanup is
342      * happening during recovery.
343      * @exception StandardException Standard Derby Error Policy
344      */

345     public void removeOldVersionOfContainers(boolean inRecovery)
346         throws StandardException
347     {
348         
349         if (inRecovery)
350         {
351             // find the old version of the container files
352
// and delete them
353
String JavaDoc[] files = dataFactory.getContainerNames();
354             if (files != null)
355             {
356                 // loop through all the files in seg0 and
357
// delete all old copies of the containers.
358
for (int i = files.length-1; i >= 0 ; i--)
359                 {
360                     // if it is a old version of the container file
361
// delete it.
362
if (isOldContainerFile(files[i]))
363                     {
364                         StorageFile oldFile = getFile(files[i]);
365                         if (!privDelete(oldFile))
366                         {
367                             throw StandardException.newException(
368                                           SQLState.FILE_CANNOT_REMOVE_FILE,
369                                           oldFile);
370                         }
371                     }
372                 }
373             }
374         }else
375         {
376             // delete all the old version of the containers.
377
for (int i = 0 ; i < noOldFiles ; i++)
378             {
379                 if (!privDelete(oldFiles[i]))
380                 {
381                     throw StandardException.newException(
382                                    SQLState.FILE_CANNOT_REMOVE_FILE,
383                                    oldFiles[i]);
384                 }
385             }
386         }
387     }
388
389
390     
391     private synchronized boolean privExists(StorageFile file)
392     {
393         actionCode = STORAGE_FILE_EXISTS_ACTION;
394         actionStorageFile = file;
395         Object JavaDoc ret = AccessController.doPrivileged(this);
396         actionStorageFile = null;
397         return ((Boolean JavaDoc) ret).booleanValue();
398
399     }
400
401     
402     private synchronized boolean privDelete(StorageFile file)
403     {
404         actionCode = STORAGE_FILE_DELETE_ACTION;
405         actionStorageFile = file;
406         Object JavaDoc ret = AccessController.doPrivileged(this);
407         actionStorageFile = null;
408         return ((Boolean JavaDoc) ret).booleanValue();
409         
410     }
411
412     private synchronized boolean privRename(StorageFile fromFile,
413                                             StorageFile destFile)
414     {
415         actionCode = STORAGE_FILE_RENAME_ACTION;
416         actionStorageFile = fromFile;
417         actionDestStorageFile = destFile;
418         Object JavaDoc ret = AccessController.doPrivileged(this);
419         actionStorageFile = null;
420         actionDestStorageFile = null;
421         return ((Boolean JavaDoc) ret).booleanValue();
422
423     }
424
425
426
427     // PrivilegedAction method
428
public Object JavaDoc run()
429     {
430         switch(actionCode)
431         {
432         case STORAGE_FILE_EXISTS_ACTION:
433             return ReuseFactory.getBoolean(actionStorageFile.exists());
434         case STORAGE_FILE_DELETE_ACTION:
435             return ReuseFactory.getBoolean(actionStorageFile.delete());
436         case STORAGE_FILE_RENAME_ACTION:
437             return ReuseFactory.getBoolean(
438                        actionStorageFile.renameTo(actionDestStorageFile));
439         }
440
441         return null;
442     }
443 }
444
Popular Tags