0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.store.raw.RawStore
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.store.raw;
0023:
0024: import org.apache.derby.iapi.services.daemon.DaemonFactory;
0025: import org.apache.derby.iapi.services.daemon.DaemonService;
0026: import org.apache.derby.iapi.services.context.ContextManager;
0027: import org.apache.derby.iapi.services.context.ContextService;
0028: import org.apache.derby.iapi.services.crypto.CipherFactoryBuilder;
0029: import org.apache.derby.iapi.services.crypto.CipherFactory;
0030: import org.apache.derby.iapi.services.crypto.CipherProvider;
0031: import org.apache.derby.iapi.services.locks.LockFactory;
0032: import org.apache.derby.iapi.services.monitor.Monitor;
0033: import org.apache.derby.iapi.services.monitor.ModuleControl;
0034: import org.apache.derby.iapi.services.monitor.ModuleSupportable;
0035: import org.apache.derby.iapi.services.monitor.PersistentService;
0036: import org.apache.derby.iapi.services.sanity.SanityManager;
0037:
0038: import org.apache.derby.iapi.error.StandardException;
0039: import org.apache.derby.iapi.services.i18n.MessageService;
0040:
0041: import org.apache.derby.iapi.services.property.PersistentSet;
0042: import org.apache.derby.iapi.store.access.TransactionInfo;
0043: import org.apache.derby.iapi.store.access.AccessFactoryGlobals;
0044: import org.apache.derby.iapi.store.access.FileResource;
0045: import org.apache.derby.iapi.store.raw.ScanHandle;
0046: import org.apache.derby.iapi.store.raw.RawStoreFactory;
0047: import org.apache.derby.iapi.store.raw.Transaction;
0048: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
0049: import org.apache.derby.iapi.store.raw.xact.TransactionFactory;
0050: import org.apache.derby.iapi.store.raw.data.DataFactory;
0051: import org.apache.derby.iapi.store.raw.log.LogFactory;
0052: import org.apache.derby.iapi.store.raw.log.LogInstant;
0053: import org.apache.derby.impl.services.monitor.UpdateServiceProperties;
0054:
0055: import org.apache.derby.io.StorageFactory;
0056: import org.apache.derby.io.WritableStorageFactory;
0057: import org.apache.derby.io.StorageFile;
0058: import org.apache.derby.iapi.store.access.DatabaseInstant;
0059: import org.apache.derby.catalog.UUID;
0060: import org.apache.derby.iapi.services.property.PropertyUtil;
0061: import org.apache.derby.iapi.services.io.FileUtil;
0062: import org.apache.derby.iapi.util.ReuseFactory;
0063: import org.apache.derby.iapi.util.StringUtil;
0064: import org.apache.derby.iapi.reference.Attribute;
0065: import org.apache.derby.iapi.reference.SQLState;
0066: import org.apache.derby.iapi.reference.MessageId;
0067: import org.apache.derby.iapi.reference.Property;
0068:
0069: import java.security.AccessController;
0070: import java.security.PrivilegedActionException;
0071: import java.security.PrivilegedExceptionAction;
0072: import java.security.SecureRandom;
0073:
0074: import java.util.Date;
0075: import java.util.Properties;
0076: import java.io.Serializable;
0077: import java.io.File;
0078: import java.io.FileOutputStream;
0079: import java.io.FileInputStream;
0080: import java.io.IOException;
0081: import java.io.FileNotFoundException;
0082: import java.io.OutputStreamWriter;
0083:
0084: import java.net.MalformedURLException;
0085: import java.net.URL;
0086:
0087: import java.security.PrivilegedExceptionAction;
0088: import java.lang.SecurityException;
0089:
0090: /**
0091: A Raw store that implements the RawStoreFactory module by delegating all the
0092: work to the lower modules TransactionFactory, LogFactory and DataFactory.
0093: <PRE>
0094: String TransactionFactoryId=<moduleIdentifier>
0095: </PRE>
0096:
0097: <P>
0098: Class is final as it has methods with privilege blocks
0099: and implements PrivilegedExceptionAction.
0100: */
0101:
0102: public final class RawStore implements RawStoreFactory, ModuleControl,
0103: ModuleSupportable, PrivilegedExceptionAction {
0104: private static final String BACKUP_HISTORY = "BACKUP.HISTORY";
0105: protected TransactionFactory xactFactory;
0106: protected DataFactory dataFactory;
0107: protected LogFactory logFactory;
0108: private StorageFactory storageFactory;
0109:
0110: private SecureRandom random;
0111: private boolean databaseEncrypted;
0112: private boolean encryptDatabase;
0113: private CipherProvider encryptionEngine;
0114: private CipherProvider decryptionEngine;
0115: private CipherProvider newEncryptionEngine;
0116: private CipherProvider newDecryptionEngine;
0117: private CipherFactory currentCipherFactory;
0118: private CipherFactory newCipherFactory = null;
0119: private int counter_encrypt;
0120: private int counter_decrypt;
0121: private int encryptionBlockSize = RawStoreFactory.DEFAULT_ENCRYPTION_BLOCKSIZE;
0122:
0123: String dataDirectory; // where files are stored
0124:
0125: // this daemon takes care of all daemon work for this raw store
0126: protected DaemonService rawStoreDaemon;
0127:
0128: private int actionCode;
0129: private static final int FILE_WRITER_ACTION = 1;
0130: private StorageFile actionStorageFile;
0131: private StorageFile actionToStorageFile;
0132: private boolean actionAppend;
0133: private static final int REGULAR_FILE_EXISTS_ACTION = 2;
0134: private File actionRegularFile;
0135: private static final int STORAGE_FILE_EXISTS_ACTION = 3;
0136: private static final int REGULAR_FILE_DELETE_ACTION = 4;
0137: private static final int REGULAR_FILE_MKDIRS_ACTION = 5;
0138: private static final int REGULAR_FILE_IS_DIRECTORY_ACTION = 6;
0139: private static final int REGULAR_FILE_REMOVE_DIRECTORY_ACTION = 7;
0140: private static final int REGULAR_FILE_RENAME_TO_ACTION = 8;
0141: private File actionRegularFile2;
0142: private static final int COPY_STORAGE_DIRECTORY_TO_REGULAR_ACTION = 9;
0143: private byte[] actionBuffer;
0144: private String[] actionFilter;
0145: private boolean actionCopySubDirs;
0146: private static final int COPY_REGULAR_DIRECTORY_TO_STORAGE_ACTION = 10;
0147: private static final int COPY_REGULAR_FILE_TO_STORAGE_ACTION = 11;
0148: private static final int REGULAR_FILE_LIST_DIRECTORY_ACTION = 12;
0149: private static final int STORAGE_FILE_LIST_DIRECTORY_ACTION = 13;
0150: private static final int COPY_STORAGE_FILE_TO_REGULAR_ACTION = 14;
0151: private static final int REGULAR_FILE_GET_CANONICALPATH_ACTION = 15;
0152: private static final int STORAGE_FILE_GET_CANONICALPATH_ACTION = 16;
0153: private static final int COPY_STORAGE_FILE_TO_STORAGE_ACTION = 17;
0154: private static final int STORAGE_FILE_DELETE_ACTION = 18;
0155:
0156: public RawStore() {
0157: }
0158:
0159: /*
0160: ** Methods of ModuleControl
0161: */
0162:
0163: /**
0164: We use this RawStore for all databases.
0165: */
0166: public boolean canSupport(Properties startParams) {
0167: return true;
0168: }
0169:
0170: public void boot(boolean create, Properties properties)
0171: throws StandardException {
0172: dataDirectory = properties.getProperty(PersistentService.ROOT);
0173: DaemonFactory daemonFactory = (DaemonFactory) Monitor
0174: .startSystemModule(org.apache.derby.iapi.reference.Module.DaemonFactory);
0175: rawStoreDaemon = daemonFactory
0176: .createNewDaemon("rawStoreDaemon");
0177: xactFactory = (TransactionFactory) Monitor
0178: .bootServiceModule(create, this ,
0179: getTransactionFactoryModule(), properties);
0180:
0181: dataFactory = (DataFactory) Monitor.bootServiceModule(create,
0182: this , getDataFactoryModule(), properties);
0183: storageFactory = dataFactory.getStorageFactory();
0184:
0185: String restoreFromBackup = null;
0186:
0187: if (properties != null) {
0188: // check if this is a restore from a backup copy.
0189: restoreFromBackup = properties
0190: .getProperty(Attribute.CREATE_FROM);
0191: if (restoreFromBackup == null)
0192: restoreFromBackup = properties
0193: .getProperty(Attribute.RESTORE_FROM);
0194: if (restoreFromBackup == null)
0195: restoreFromBackup = properties
0196: .getProperty(Attribute.ROLL_FORWARD_RECOVERY_FROM);
0197:
0198: }
0199:
0200: // setup database encryption engines.
0201: if (create)
0202: setupEncryptionEngines(create, properties);
0203:
0204: // let everyone knows who their rawStoreFactory is and they can use it
0205: // to get to other modules
0206: // pass in create and properties to dataFactory so it can boot the log
0207: // factory
0208:
0209: dataFactory.setRawStoreFactory(this , create, properties);
0210: xactFactory.setRawStoreFactory(this );
0211:
0212: if (properties instanceof UpdateServiceProperties) {
0213: if (storageFactory instanceof WritableStorageFactory)
0214: ((UpdateServiceProperties) properties)
0215: .setStorageFactory((WritableStorageFactory) storageFactory);
0216: }
0217:
0218: // log factory is booted by the data factory
0219: logFactory = (LogFactory) Monitor.findServiceModule(this ,
0220: getLogFactoryModule());
0221:
0222: //save the service properties to a file if we are doing a restore from
0223: if (restoreFromBackup != null) {
0224: //copy the jar files.etc from backup if they don't exist
0225: restoreRemainingFromBackup(restoreFromBackup);
0226: ((UpdateServiceProperties) properties)
0227: .saveServiceProperties();
0228: }
0229:
0230: // If the log is at another location, make sure service.properties
0231: // file has it.
0232: String logDevice = properties.getProperty(Attribute.LOG_DEVICE);
0233: if (logDevice != null) {
0234: if (!isReadOnly() // We do not care about log location if read only
0235: && (create
0236: || !logDevice.equals(logFactory
0237: .getCanonicalLogPath()) || restoreFromBackup != null)) {
0238: // get the real location from the log factory
0239: properties.put(Attribute.LOG_DEVICE, logFactory
0240: .getCanonicalLogPath());
0241: //make the log device param stored in backup is same as current log device.
0242: properties.put(Property.LOG_DEVICE_AT_BACKUP,
0243: logFactory.getCanonicalLogPath());
0244: }
0245:
0246: } else {
0247: //when we restore from a backup logDevice param does not exists
0248: //in service.properties to support restore using OS commands to work.
0249: //Instead of logDevice, we user logDeviceWhenBackedUp parameter to
0250: //identify the log location while restoring createFrom/restoreFrom/rollForwardRecoveryFrom
0251: //attribute , following make sures the logDevice parameter gets
0252: //into service.propertues in such cases.
0253: if (restoreFromBackup != null
0254: && logFactory.getCanonicalLogPath() != null) {
0255: //logdevice might have got changed because of backup restore.
0256: properties.put(Attribute.LOG_DEVICE, logFactory
0257: .getCanonicalLogPath());
0258: } else {
0259: //might have been OS copy restore. We default log to db home
0260: properties.remove(Property.LOG_DEVICE_AT_BACKUP);
0261: }
0262: }
0263:
0264: //save the service properties to a file if we are doing a restore from
0265: if (restoreFromBackup != null) {
0266: //copy the jar files.etc from backup if they don't exist
0267: restoreRemainingFromBackup(restoreFromBackup);
0268: ((UpdateServiceProperties) properties)
0269: .saveServiceProperties();
0270: }
0271:
0272: /**
0273: * Note: service.properties file acts as flags to indicate
0274: * that the copy from backup is successful.
0275: * If we reached so far while restoring from backup means
0276: * we copied all the necessary data from backup. Only thing
0277: * that remains is roll forwarding the logs. Incase if we crash at this
0278: * point and user re boots the datbase again without any restore flags
0279: * it shoud boot without any problem.
0280: **/
0281:
0282: // setup database encryption engine
0283: if (!create) {
0284: // check if the engine crashed while re-encrypting an
0285: // encrypted database or while encryption and
0286: // existing database.
0287: if (properties
0288: .getProperty(RawStoreFactory.DB_ENCRYPTION_STATUS) != null) {
0289: handleIncompleteDatabaseEncryption(properties);
0290: }
0291:
0292: setupEncryptionEngines(create, properties);
0293: }
0294:
0295: if (databaseEncrypted) {
0296: // let log factory know if the database is encrypted .
0297: logFactory.setDatabaseEncrypted(false);
0298: // let data factory know if the database is encrypted.
0299: dataFactory.setDatabaseEncrypted();
0300: }
0301:
0302: // no need to tell log factory which raw store factory it belongs to
0303: // since this is passed into the log factory for recovery
0304: // after the factories are loaded, recover the database
0305: logFactory.recover(this , dataFactory, xactFactory);
0306:
0307: // if user requested to encrpty an unecrypted database or encrypt with
0308: // new alogorithm then do that now.
0309: if (encryptDatabase) {
0310: configureDatabaseForEncryption(properties, newCipherFactory);
0311: }
0312: }
0313:
0314: public void stop() {
0315:
0316: if (SanityManager.DEBUG) {
0317: if (databaseEncrypted)
0318: SanityManager.DEBUG_PRINT("encryption statistics",
0319: "Encryption called " + counter_encrypt
0320: + " times, " + "decryption called "
0321: + counter_decrypt + " times");
0322: }
0323:
0324: if (rawStoreDaemon != null)
0325: rawStoreDaemon.stop();
0326:
0327: if (logFactory == null)
0328: return;
0329:
0330: try {
0331:
0332: if (logFactory.checkpoint(this , dataFactory, xactFactory,
0333: false)) {
0334: if (dataFactory != null)
0335: dataFactory.removeStubsOK();
0336: }
0337:
0338: } catch (StandardException se) {
0339: // checkpoint failed, stop all factory from shutting down normally
0340: markCorrupt(se);
0341: }
0342: }
0343:
0344: /*
0345: ** Methods of RawStoreFactory
0346: */
0347:
0348: /**
0349: Is the store read-only.
0350: @see RawStoreFactory#isReadOnly
0351: */
0352: public boolean isReadOnly() {
0353: return dataFactory.isReadOnly();
0354: }
0355:
0356: public LockFactory getLockFactory() {
0357: return xactFactory.getLockFactory();
0358: }
0359:
0360: /**
0361: Get the Transaction Factory to use with this store.
0362: */
0363: public TransactionFactory getXactFactory() {
0364: return xactFactory;
0365: }
0366:
0367: /*
0368: * Return the module providing XAresource interface to the transaction
0369: * table.
0370: *
0371: * @exception StandardException Standard cloudscape exception policy.
0372: */
0373: public/* XAResourceManager */Object getXAResourceManager()
0374: throws StandardException {
0375: return (xactFactory.getXAResourceManager());
0376: }
0377:
0378: public Transaction startGlobalTransaction(
0379: ContextManager contextMgr, int format_id, byte[] global_id,
0380: byte[] branch_id) throws StandardException {
0381: return xactFactory.startGlobalTransaction(this , contextMgr,
0382: format_id, global_id, branch_id);
0383: }
0384:
0385: public Transaction startTransaction(ContextManager contextMgr,
0386: String transName) throws StandardException {
0387: return xactFactory
0388: .startTransaction(this , contextMgr, transName);
0389: }
0390:
0391: public Transaction startNestedReadOnlyUserTransaction(
0392: Object compatibilitySpace, ContextManager contextMgr,
0393: String transName) throws StandardException {
0394: return (xactFactory.startNestedReadOnlyUserTransaction(this ,
0395: compatibilitySpace, contextMgr, transName));
0396: }
0397:
0398: public Transaction startNestedUpdateUserTransaction(
0399: ContextManager contextMgr, String transName)
0400: throws StandardException {
0401: return (xactFactory.startNestedUpdateUserTransaction(this ,
0402: contextMgr, transName));
0403: }
0404:
0405: public Transaction findUserTransaction(ContextManager contextMgr,
0406: String transName) throws StandardException {
0407: return xactFactory.findUserTransaction(this , contextMgr,
0408: transName);
0409: }
0410:
0411: public Transaction startInternalTransaction(
0412: ContextManager contextMgr) throws StandardException {
0413:
0414: return xactFactory.startInternalTransaction(this , contextMgr);
0415: }
0416:
0417: public void checkpoint() throws StandardException {
0418: logFactory.checkpoint(this , dataFactory, xactFactory, false);
0419: }
0420:
0421: public void freeze() throws StandardException {
0422: logFactory.checkpoint(this , dataFactory, xactFactory, true);
0423: dataFactory.freezePersistentStore();
0424: logFactory.freezePersistentStore();
0425: }
0426:
0427: public void unfreeze() throws StandardException {
0428: logFactory.unfreezePersistentStore();
0429: dataFactory.unfreezePersistentStore();
0430: }
0431:
0432: /**
0433: * Backup the database to a backup directory.
0434: *
0435: * @param backupDir the name of the directory where the backup should be
0436: * stored. This directory will be created if it
0437: * does not exist.
0438: * @param wait if <tt>true</tt>, waits for all the backup blocking
0439: * operations in progress to finish.
0440: * @exception StandardException thrown on error
0441: */
0442: public void backup(String backupDir, boolean wait)
0443: throws StandardException {
0444: if (backupDir == null || backupDir.equals("")) {
0445: throw StandardException.newException(
0446: SQLState.RAWSTORE_CANNOT_CREATE_BACKUP_DIRECTORY,
0447: (File) null);
0448: }
0449:
0450: // in case this is an URL form
0451: String backupDirURL = null;
0452: try {
0453: URL url = new URL(backupDir);
0454: backupDirURL = url.getFile();
0455: } catch (MalformedURLException ex) {
0456: }
0457:
0458: if (backupDirURL != null)
0459: backupDir = backupDirURL;
0460:
0461: // find the user transaction, it is necessary for online backup
0462: // to open the container through page cache
0463: RawTransaction t = xactFactory.findUserTransaction(this ,
0464: ContextService.getFactory().getCurrentContextManager(),
0465: AccessFactoryGlobals.USER_TRANS_NAME);
0466:
0467: try {
0468:
0469: // check if any backup blocking operations are in progress
0470: // in the same transaction backup is being executed? Backup is
0471: // not allowed if the transaction has uncommitted
0472: // unlogged operations that are blocking the backup.
0473:
0474: if (t.isBlockingBackup()) {
0475: throw StandardException
0476: .newException(SQLState.BACKUP_OPERATIONS_NOT_ALLOWED);
0477: }
0478:
0479: // check if any backup blocking operations are in progress
0480: // and stop new ones from starting until the backup is completed.
0481: if (!xactFactory.blockBackupBlockingOperations(wait)) {
0482: throw StandardException
0483: .newException(SQLState.BACKUP_BLOCKING_OPERATIONS_IN_PROGRESS);
0484: }
0485:
0486: // perform backup
0487: backup(t, new File(backupDir));
0488: } finally {
0489: // let the xactfatory know that backup is done, so that
0490: // it can allow backup blocking operations.
0491: xactFactory.unblockBackupBlockingOperations();
0492: }
0493: }
0494:
0495: /*
0496: * Backup the database.
0497: * Online backup copies all the database files (log, seg0 ...Etc) to the
0498: * specified backup location without blocking any user operation for the
0499: * duration of the backup. Stable copy is made of each page using
0500: * page level latches and in some cases with the help of monitors.
0501: * Transaction log is also backed up, this is used to bring the database to
0502: * the consistent state on restore.
0503: *
0504: * <P> MT- only one thread is allowed to perform backup at any given time.
0505: * Synchronized on this. Parallel backups are not supported.
0506: */
0507: public synchronized void backup(Transaction t, File backupDir)
0508: throws StandardException {
0509: if (!privExists(backupDir)) {
0510: // if backup dir does not exist, go ahead and create it.
0511:
0512: if (!privMkdirs(backupDir)) {
0513: throw StandardException
0514: .newException(
0515: SQLState.RAWSTORE_CANNOT_CREATE_BACKUP_DIRECTORY,
0516: (File) backupDir);
0517: }
0518: } else {
0519: // entity with backup name exists, make sure it is a directory.
0520:
0521: if (!privIsDirectory(backupDir)) {
0522: throw StandardException
0523: .newException(
0524: SQLState.RAWSTORE_CANNOT_BACKUP_TO_NONDIRECTORY,
0525: (File) backupDir);
0526: }
0527:
0528: // check if a user has given the backup as a database directory by
0529: // mistake, backup path can not be a derby database directory.
0530: // If a directory contains PersistentService.PROPERTIES_NAME, it
0531: // is assumed to be a derby database directory because derby
0532: // databases always have this file.
0533:
0534: if (privExists(new File(backupDir,
0535: PersistentService.PROPERTIES_NAME))) {
0536: throw StandardException
0537: .newException(
0538: SQLState.RAWSTORE_CANNOT_BACKUP_INTO_DATABASE_DIRECTORY,
0539: (File) backupDir);
0540: }
0541: }
0542:
0543: boolean error = true;
0544: boolean renamed = false;
0545: boolean renameFailed = false;
0546: File oldbackup = null;
0547: File backupcopy = null;
0548: OutputStreamWriter historyFile = null;
0549: StorageFile dbHistoryFile = null;
0550: File backupHistoryFile = null;
0551: LogInstant backupInstant = logFactory
0552: .getFirstUnflushedInstant();
0553:
0554: try {
0555: // get name of the current db, ie. database directory of current db.
0556: StorageFile dbase = storageFactory.newStorageFile(null);
0557: String canonicalDbName = storageFactory.getCanonicalName();
0558: int lastSep = canonicalDbName.lastIndexOf(storageFactory
0559: .getSeparator());
0560: String dbname = canonicalDbName.substring(lastSep + 1);
0561:
0562: // append to end of history file
0563: historyFile = privFileWriter(storageFactory
0564: .newStorageFile(BACKUP_HISTORY), true);
0565:
0566: backupcopy = new File(backupDir, dbname);
0567:
0568: logHistory(historyFile, MessageService.getTextMessage(
0569: MessageId.STORE_BACKUP_STARTED, canonicalDbName,
0570: getFilePath(backupcopy)));
0571:
0572: // check if a backup copy of this database already exists,
0573: if (privExists(backupcopy)) {
0574: // first make a backup of the backup
0575: oldbackup = new File(backupDir, dbname + ".OLD");
0576: if (privExists(oldbackup)) {
0577: if (privIsDirectory(oldbackup))
0578: privRemoveDirectory(oldbackup);
0579: else
0580: privDelete(oldbackup);
0581: }
0582:
0583: if (!privRenameTo(backupcopy, oldbackup)) {
0584: renameFailed = true;
0585: throw StandardException.newException(
0586: SQLState.RAWSTORE_ERROR_RENAMING_FILE,
0587: backupcopy, oldbackup);
0588: } else {
0589: logHistory(historyFile, MessageService
0590: .getTextMessage(
0591: MessageId.STORE_MOVED_BACKUP,
0592: getFilePath(backupcopy),
0593: getFilePath(oldbackup)));
0594: renamed = true;
0595: }
0596: }
0597:
0598: // create the backup database directory
0599: if (!privMkdirs(backupcopy)) {
0600: throw StandardException
0601: .newException(
0602: SQLState.RAWSTORE_CANNOT_CREATE_BACKUP_DIRECTORY,
0603: (File) backupcopy);
0604: }
0605:
0606: dbHistoryFile = storageFactory
0607: .newStorageFile(BACKUP_HISTORY);
0608: backupHistoryFile = new File(backupcopy, BACKUP_HISTORY);
0609:
0610: // copy the history file into the backup.
0611: if (!privCopyFile(dbHistoryFile, backupHistoryFile))
0612: throw StandardException.newException(
0613: SQLState.RAWSTORE_ERROR_COPYING_FILE,
0614: dbHistoryFile, backupHistoryFile);
0615:
0616: // if they are any jar file stored in the database, copy them into
0617: // the backup.
0618: StorageFile jarDir = storageFactory
0619: .newStorageFile(FileResource.JAR_DIRECTORY_NAME);
0620:
0621: if (privExists(jarDir)) {
0622: // find the list of schema directories under the jar dir and
0623: // then copy only the plain files under those directories. One
0624: // could just use the recursive copy of directory to copy all
0625: // the files under the jar dir, but the problem with that is if
0626: // a user gives jar directory as the backup path by mistake,
0627: // copy will fail while copying the backup dir onto itself in
0628: // recursion
0629:
0630: String[] jarSchemaList = privList(jarDir);
0631: File backupJarDir = new File(backupcopy,
0632: FileResource.JAR_DIRECTORY_NAME);
0633: // Create the backup jar directory
0634: if (!privMkdirs(backupJarDir)) {
0635: throw StandardException
0636: .newException(
0637: SQLState.RAWSTORE_CANNOT_CREATE_BACKUP_DIRECTORY,
0638: (File) backupJarDir);
0639: }
0640:
0641: for (int i = 0; i < jarSchemaList.length; i++) {
0642: StorageFile jarSchemaDir = storageFactory
0643: .newStorageFile(jarDir, jarSchemaList[i]);
0644: File backupJarSchemaDir = new File(backupJarDir,
0645: jarSchemaList[i]);
0646:
0647: if (!privCopyDirectory(jarSchemaDir,
0648: backupJarSchemaDir, (byte[]) null, null,
0649: false)) {
0650: throw StandardException.newException(
0651: SQLState.RAWSTORE_ERROR_COPYING_FILE,
0652: jarSchemaDir, backupJarSchemaDir);
0653: }
0654: }
0655: }
0656:
0657: // save service properties into the backup, Read in property
0658: // from service.properties file, remove logDevice from it,
0659: // then write it to the backup.
0660:
0661: StorageFile logdir = logFactory.getLogDirectory();
0662:
0663: try {
0664: String name = Monitor.getMonitor().getServiceName(this );
0665: PersistentService ps = Monitor.getMonitor()
0666: .getServiceType(this );
0667: String fullName = ps.getCanonicalServiceName(name);
0668: Properties prop = ps.getServiceProperties(fullName,
0669: (Properties) null);
0670:
0671: StorageFile defaultLogDir = storageFactory
0672: .newStorageFile(LogFactory.LOG_DIRECTORY_NAME);
0673:
0674: if (!logdir.equals(defaultLogDir)) {
0675: prop.remove(Attribute.LOG_DEVICE);
0676: if (SanityManager.DEBUG) {
0677: SanityManager
0678: .ASSERT(
0679: prop
0680: .getProperty(Attribute.LOG_DEVICE) == null,
0681: "cannot get rid of logDevice property");
0682: }
0683:
0684: logHistory(
0685: historyFile,
0686: MessageService
0687: .getTextMessage(MessageId.STORE_EDITED_SERVICEPROPS));
0688: }
0689:
0690: // save the service properties into the backup.
0691: ps.saveServiceProperties(backupcopy.getPath(), prop,
0692: false);
0693:
0694: } catch (StandardException se) {
0695: logHistory(
0696: historyFile,
0697: MessageService
0698: .getTextMessage(MessageId.STORE_ERROR_EDIT_SERVICEPROPS)
0699: + se);
0700:
0701: return; // skip the rest and let finally block clean up
0702: }
0703:
0704: // Incase of encrypted database and the key is an external
0705: // encryption key, there is an extra file with name
0706: // Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE, this file should be
0707: // copied in to the backup.
0708: StorageFile verifyKeyFile = storageFactory
0709: .newStorageFile(Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE);
0710: if (privExists(verifyKeyFile)) {
0711: File backupVerifyKeyFile = new File(backupcopy,
0712: Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE);
0713:
0714: if (!privCopyFile(verifyKeyFile, backupVerifyKeyFile))
0715: throw StandardException.newException(
0716: SQLState.RAWSTORE_ERROR_COPYING_FILE,
0717: verifyKeyFile, backupVerifyKeyFile);
0718: }
0719:
0720: File logBackup = new File(backupcopy,
0721: LogFactory.LOG_DIRECTORY_NAME);
0722:
0723: // this is wierd, delete it
0724: if (privExists(logBackup)) {
0725: privRemoveDirectory(logBackup);
0726: }
0727:
0728: // Create the log directory
0729: if (!privMkdirs(logBackup)) {
0730: throw StandardException
0731: .newException(
0732: SQLState.RAWSTORE_CANNOT_CREATE_BACKUP_DIRECTORY,
0733: (File) logBackup);
0734: }
0735:
0736: // do a checkpoint to get the persistent store up to date.
0737: logFactory.checkpoint(this , dataFactory, xactFactory, true);
0738:
0739: // start the transaction log backup.
0740: logFactory.startLogBackup(logBackup);
0741:
0742: File segBackup = new File(backupcopy, "seg0");
0743:
0744: // Create the data segment directory
0745: if (!privMkdirs(segBackup)) {
0746: throw StandardException
0747: .newException(
0748: SQLState.RAWSTORE_CANNOT_CREATE_BACKUP_DIRECTORY,
0749: (File) segBackup);
0750: }
0751:
0752: // backup all the information in the data segment.
0753: dataFactory.backupDataFiles(t, segBackup);
0754:
0755: logHistory(historyFile, MessageService.getTextMessage(
0756: MessageId.STORE_DATA_SEG_BACKUP_COMPLETED,
0757: getFilePath(segBackup)));
0758:
0759: // copy the log that got generated after the backup started to
0760: // backup location and tell the logfactory that backup has come
0761: // to end.
0762: logFactory.endLogBackup(logBackup);
0763:
0764: logHistory(historyFile, MessageService.getTextMessage(
0765: MessageId.STORE_COPIED_LOG, getFilePath(logdir),
0766: getFilePath(logBackup)));
0767:
0768: error = false;
0769: } catch (IOException ioe) {
0770: throw StandardException.newException(
0771: SQLState.RAWSTORE_UNEXPECTED_EXCEPTION, ioe);
0772: } finally {
0773:
0774: try {
0775: if (error) {
0776:
0777: // Abort all activity related to backup in the log factory.
0778: logFactory.abortLogBackup();
0779:
0780: // remove the half backed up copy
0781: // unless the error occured during rename process;
0782: // inwhich case 'backupcopy' refers to the previous backup
0783: // not an half backed one.
0784: if (!renameFailed)
0785: privRemoveDirectory(backupcopy);
0786:
0787: if (renamed)
0788: // recover the old backup
0789: privRenameTo(oldbackup, backupcopy);
0790:
0791: logHistory(
0792: historyFile,
0793: MessageService
0794: .getTextMessage(MessageId.STORE_BACKUP_ABORTED));
0795:
0796: } else {
0797: // success, remove the old backup copy
0798: if (renamed && privExists(oldbackup)) {
0799: // get rid of the old backup
0800: privRemoveDirectory(oldbackup);
0801: logHistory(historyFile, MessageService
0802: .getTextMessage(
0803: MessageId.STORE_REMOVED_BACKUP,
0804: getFilePath(oldbackup)));
0805: }
0806: logHistory(historyFile, MessageService
0807: .getTextMessage(
0808: MessageId.STORE_BACKUP_COMPLETED,
0809: backupInstant));
0810:
0811: // copy the updated version of history file with current
0812: // backup information into the backup.
0813: if (!privCopyFile(dbHistoryFile, backupHistoryFile))
0814: throw StandardException.newException(
0815: SQLState.RAWSTORE_ERROR_COPYING_FILE,
0816: dbHistoryFile, backupHistoryFile);
0817: }
0818:
0819: historyFile.close();
0820: } catch (IOException ioe) {
0821: try {
0822: historyFile.close();
0823: } catch (IOException ioe2) {
0824: }
0825: ;
0826: throw StandardException.newException(
0827: SQLState.RAWSTORE_UNEXPECTED_EXCEPTION, ioe);
0828: }
0829: }
0830:
0831: }
0832:
0833: /**
0834: * Backup the database to a backup directory and enable the log archive
0835: * mode that will keep the archived log files required for roll-forward
0836: * from this version backup.
0837: *
0838: * @param backupDir the name of the directory where the backup should be
0839: * stored. This directory will be created if it
0840: * does not exist.
0841: *
0842: * @param deleteOnlineArchivedLogFiles
0843: * If true deletes online archived
0844: * log files that exist before this backup, delete
0845: * will occur only after the backup is complete.
0846: *
0847: * @param wait if <tt>true</tt>, waits for all the backup blocking
0848: * operations in progress to finish.
0849: *
0850: * @exception StandardException thrown on error.
0851: */
0852: public void backupAndEnableLogArchiveMode(String backupDir,
0853: boolean deleteOnlineArchivedLogFiles, boolean wait)
0854: throws StandardException {
0855: boolean enabledLogArchive = false;
0856: try {
0857: // Enable the log archive mode, if it is not already enabled.
0858: if (!logFactory.logArchived()) {
0859: logFactory.enableLogArchiveMode();
0860: enabledLogArchive = true;
0861: }
0862:
0863: backup(backupDir, wait);
0864:
0865: // After successful backup delete the archived log files
0866: // that are not necessary to do a roll-forward recovery
0867: // from this backup if requested.
0868: if (deleteOnlineArchivedLogFiles) {
0869: logFactory.deleteOnlineArchivedLogFiles();
0870: }
0871: } catch (Throwable error) {
0872: // On any errors , disable the log archive, if it
0873: // is enabled on this call.
0874: if (enabledLogArchive)
0875: logFactory.disableLogArchiveMode();
0876: throw StandardException.plainWrapException(error);
0877: }
0878: }
0879:
0880: /*
0881: * Disable the log archive mode and delete the archived log files
0882: * if requested.
0883: *
0884: * @param deleteOnlineArchivedLogFiles
0885: * If true deletes online archived
0886: * log files that exist before this backup, delete
0887: * will occur only after the backup is complete.
0888: * @exception StandardException thrown on error.
0889: */
0890: public void disableLogArchiveMode(
0891: boolean deleteOnlineArchivedLogFiles)
0892: throws StandardException {
0893: logFactory.disableLogArchiveMode();
0894: if (deleteOnlineArchivedLogFiles) {
0895: logFactory.deleteOnlineArchivedLogFiles();
0896: }
0897: }
0898:
0899: //copies the files from the backup that does not need
0900: //any special handling like jars.
0901: private void restoreRemainingFromBackup(String backupPath)
0902: throws StandardException {
0903: /**
0904: *copy the files from the backup except the ones that we already
0905: *copied in the boot methods(like log directory and data segments)
0906: *AND Service.properties file which we create last to
0907: *indicate the end of copy from backup.
0908: */
0909:
0910: File backuploc = new File(backupPath);
0911: String[] fromList = privList(backuploc);
0912: for (int i = 0; i < fromList.length; i++) {
0913: StorageFile toFile = storageFactory
0914: .newStorageFile(fromList[i]);
0915: if (privExists(toFile)
0916: || fromList[i]
0917: .equals(PersistentService.PROPERTIES_NAME)) {
0918: continue;
0919: }
0920:
0921: File fromFile = new File(backuploc, fromList[i]);
0922: if (privIsDirectory(fromFile)) {
0923: if (!privCopyDirectory(fromFile, toFile)) {
0924: throw StandardException.newException(
0925: SQLState.UNABLE_TO_COPY_FILE_FROM_BACKUP,
0926: fromFile, toFile);
0927: }
0928: } else {
0929: if (!privCopyFile(fromFile, toFile)) {
0930: throw StandardException.newException(
0931: SQLState.UNABLE_TO_COPY_FILE_FROM_BACKUP,
0932: fromFile, toFile);
0933: }
0934: }
0935: }
0936: }
0937:
0938: public void idle() throws StandardException {
0939: dataFactory.idle();
0940: }
0941:
0942: public TransactionInfo[] getTransactionInfo() {
0943: return xactFactory.getTransactionInfo();
0944: }
0945:
0946: public ScanHandle openFlushedScan(DatabaseInstant start,
0947: int groupsIWant) throws StandardException {
0948: return logFactory.openFlushedScan(start, groupsIWant);
0949: }
0950:
0951: public DaemonService getDaemon() {
0952: return rawStoreDaemon;
0953: }
0954:
0955: public void createFinished() throws StandardException {
0956: xactFactory.createFinished();
0957: dataFactory.createFinished();
0958: }
0959:
0960: /**
0961: * Get JBMS properties relavent to raw store
0962: * @exception StandardException Standard Cloudscape Error Policy
0963: */
0964: public void getRawStoreProperties(PersistentSet set)
0965: throws StandardException {
0966: logFactory.getLogFactoryProperties(set);
0967: }
0968:
0969: /*
0970: ** backup restore
0971: */
0972: /**
0973: Freeze persistent store. Reads can still happen, only cannot write.
0974: @exception StandardException Standard Cloudscape Error Policy
0975: */
0976: public void freezePersistentStore() throws StandardException {
0977: // do a checkpoint to get the persistent store up to date.
0978: logFactory.checkpoint(this , dataFactory, xactFactory, true);
0979: logFactory.freezePersistentStore();
0980:
0981: }
0982:
0983: /**
0984: Freeze persistent store. Reads can still happen, only cannot write.
0985: @exception StandardException Standard Cloudscape Error Policy
0986: */
0987: public void unfreezePersistentStore() throws StandardException {
0988: logFactory.unfreezePersistentStore();
0989: }
0990:
0991: /*
0992: ** data encryption/decryption support
0993: */
0994:
0995: /*
0996: * Setup Encryption Engines.
0997: */
0998: private void setupEncryptionEngines(boolean create,
0999: Properties properties) throws StandardException {
1000:
1001: // check if user has requested to encrypt the database or it is an
1002: // encrypted database.
1003:
1004: String dataEncryption = properties
1005: .getProperty(Attribute.DATA_ENCRYPTION);
1006: databaseEncrypted = Boolean.valueOf(dataEncryption)
1007: .booleanValue();
1008:
1009: boolean reEncrypt = false;
1010:
1011: if (!create) {
1012: // check if database is already encrypted, by directly peeking at the
1013: // database service propertes instead of the properties passed
1014: // to this method. By looking at properties to the boot method ,
1015: // one can not differentiate if user is requesting for database
1016: // encryption or the database is already encrypted because
1017: // Attribute.DATA_ENCRYPTION is used to store in the
1018: // service properties to indicate that database
1019: // is encrypted and also users can specify it as URL attribute
1020: // to encrypt and existing database.
1021:
1022: String name = Monitor.getMonitor().getServiceName(this );
1023: PersistentService ps = Monitor.getMonitor().getServiceType(
1024: this );
1025: String canonicalName = ps.getCanonicalServiceName(name);
1026: Properties serviceprops = ps.getServiceProperties(
1027: canonicalName, (Properties) null);
1028: dataEncryption = serviceprops
1029: .getProperty(Attribute.DATA_ENCRYPTION);
1030: boolean encryptedDatabase = Boolean.valueOf(dataEncryption)
1031: .booleanValue();
1032:
1033: if (!encryptedDatabase && databaseEncrypted) {
1034: // it it not an encrypted database, user is asking to
1035: // encrypt an un-encrypted database.
1036: encryptDatabase = true;
1037: // set database as un-encrypted, we will set it as encrypted
1038: // after encrypting the existing data.
1039: databaseEncrypted = false;
1040: } else {
1041: // check if the user has requested to renecrypt an
1042: // encrypted datbase with new encryption password/key.
1043: if (encryptedDatabase) {
1044: if (properties
1045: .getProperty(Attribute.NEW_BOOT_PASSWORD) != null) {
1046: reEncrypt = true;
1047: } else if (properties
1048: .getProperty(Attribute.NEW_CRYPTO_EXTERNAL_KEY) != null) {
1049: reEncrypt = true;
1050: }
1051: ;
1052: encryptDatabase = reEncrypt;
1053: }
1054:
1055: }
1056:
1057: // NOTE: if user specifies Attribute.DATA_ENCRYPTION on the
1058: // connection URL by mistake on an already encrypted database,
1059: // it is ignored.
1060:
1061: // prevent attempt to (re)encrypt of a read-only database
1062: if (encryptDatabase) {
1063: if (isReadOnly()) {
1064: if (reEncrypt)
1065: throw StandardException
1066: .newException(SQLState.CANNOT_REENCRYPT_READONLY_DATABASE);
1067: else
1068: throw StandardException
1069: .newException(SQLState.CANNOT_ENCRYPT_READONLY_DATABASE);
1070: }
1071: }
1072: }
1073:
1074: // setup encryption engines.
1075: if (databaseEncrypted || encryptDatabase) {
1076: // check if database is configured for encryption, during
1077: // configuration some of the properties database; so that
1078: // user does not have to specify them on the URL everytime.
1079: // Incase of re-encryption of an already of encrypted database
1080: // only some information needs to updated; it is not treated
1081: // like the configuring the database for encryption first time.
1082: boolean setupEncryption = create
1083: || (encryptDatabase && !reEncrypt);
1084:
1085: // start the cipher factory module, that is is used to create
1086: // instances of the cipher factory with specific enctyption
1087: // properties.
1088:
1089: CipherFactoryBuilder cb = (CipherFactoryBuilder) Monitor
1090: .startSystemModule(org.apache.derby.iapi.reference.Module.CipherFactoryBuilder);
1091:
1092: // create instance of the cipher factory with the
1093: // specified encryption properties.
1094: currentCipherFactory = cb.createCipherFactory(
1095: setupEncryption, properties, false);
1096:
1097: // The database can be encrypted using an encryption key that is given at
1098: // connection url. For security reasons, this key is not made persistent
1099: // in the database. But it is necessary to verify the encryption key
1100: // whenever booting the database if it is similar to the key that was used
1101: // during creation time. This needs to happen before we access the data/logs to
1102: // avoid the risk of corrupting the database because of a wrong encryption key.
1103:
1104: // Please note this verification process does not provide any added security
1105: // but is intended to allow to fail gracefully if a wrong encryption key
1106: // is used during boot time
1107:
1108: currentCipherFactory.verifyKey(setupEncryption,
1109: storageFactory, properties);
1110:
1111: // Initializes the encryption and decryption engines
1112: encryptionEngine = currentCipherFactory
1113: .createNewCipher(CipherFactory.ENCRYPT);
1114:
1115: // At creation time of an encrypted database, store the encryption block size
1116: // for the algorithm. Store this value as property given by
1117: // RawStoreFactory.ENCRYPTION_BLOCKSIZE. This value
1118: // is made persistent by storing it in service.properties
1119: // To connect to an existing database, retrieve the value and use it for
1120: // appropriate padding.
1121: // The default value of encryption block size is 8,
1122: // to allow for downgrade issues
1123: // Before support for AES (beetle6023), default encryption block size supported
1124: // was 8
1125:
1126: if (setupEncryption) {
1127: encryptionBlockSize = encryptionEngine
1128: .getEncryptionBlockSize();
1129: // in case of database create, store the encryption block
1130: // size. Incase of reconfiguring the existing datbase, this
1131: // will be saved after encrypting the exisiting data.
1132: if (create)
1133: properties.put(
1134: RawStoreFactory.ENCRYPTION_BLOCKSIZE,
1135: String.valueOf(encryptionBlockSize));
1136: } else {
1137: if (properties
1138: .getProperty(RawStoreFactory.ENCRYPTION_BLOCKSIZE) != null)
1139: encryptionBlockSize = Integer
1140: .parseInt(properties
1141: .getProperty(RawStoreFactory.ENCRYPTION_BLOCKSIZE));
1142: else
1143: encryptionBlockSize = encryptionEngine
1144: .getEncryptionBlockSize();
1145: }
1146:
1147: decryptionEngine = currentCipherFactory
1148: .createNewCipher(CipherFactory.DECRYPT);
1149:
1150: random = currentCipherFactory.getSecureRandom();
1151:
1152: if (encryptDatabase) {
1153:
1154: if (reEncrypt) {
1155: // create new cipher factory with the new encrytpion
1156: // properties specified by the user. This cipher factory
1157: // is used to create the new encryption/decryption
1158: // engines to reencrypt the database with the new
1159: // encryption keys.
1160: newCipherFactory = cb.createCipherFactory(
1161: setupEncryption, properties, true);
1162: newDecryptionEngine = newCipherFactory
1163: .createNewCipher(CipherFactory.DECRYPT);
1164: newEncryptionEngine = newCipherFactory
1165: .createNewCipher(CipherFactory.ENCRYPT);
1166: } else {
1167: // there is only one engine when configuring an
1168: // unencrypted database for encryption
1169: newDecryptionEngine = decryptionEngine;
1170: newEncryptionEngine = encryptionEngine;
1171:
1172: }
1173: }
1174:
1175: // save the encryption properties if encryption is enabled
1176: // at database creation time.
1177: if (create)
1178: currentCipherFactory.saveProperties(properties);
1179: }
1180: }
1181:
1182: /**
1183: Encrypt cleartext into ciphertext.
1184:
1185: @see CipherProvider#encrypt
1186:
1187: @exception StandardException Standard Cloudscape Error Policy
1188: */
1189: public int encrypt(byte[] cleartext, int offset, int length,
1190: byte[] ciphertext, int outputOffset, boolean newEngine)
1191: throws StandardException {
1192: if ((databaseEncrypted == false && encryptDatabase == false)
1193: || (encryptionEngine == null && newEncryptionEngine == null)) {
1194: throw StandardException
1195: .newException(SQLState.STORE_FEATURE_NOT_IMPLEMENTED);
1196: }
1197:
1198: counter_encrypt++;
1199:
1200: if (newEngine) {
1201: return newEncryptionEngine.encrypt(cleartext, offset,
1202: length, ciphertext, outputOffset);
1203: } else {
1204: return encryptionEngine.encrypt(cleartext, offset, length,
1205: ciphertext, outputOffset);
1206: }
1207: }
1208:
1209: /**
1210: Decrypt cleartext from ciphertext.
1211:
1212: @see CipherProvider#decrypt
1213:
1214: @exception StandardException Standard Cloudscape Error Policy
1215: */
1216: public int decrypt(byte[] ciphertext, int offset, int length,
1217: byte[] cleartext, int outputOffset)
1218: throws StandardException {
1219: if (databaseEncrypted == false || decryptionEngine == null) {
1220: throw StandardException
1221: .newException(SQLState.STORE_FEATURE_NOT_IMPLEMENTED);
1222: }
1223:
1224: counter_decrypt++;
1225: return decryptionEngine.decrypt(ciphertext, offset, length,
1226: cleartext, outputOffset);
1227: }
1228:
1229: /**
1230: Returns the encryption block size used by the algorithm at time of
1231: creation of an encrypted database
1232: */
1233: public int getEncryptionBlockSize() {
1234: return encryptionBlockSize;
1235: }
1236:
1237: public int random() {
1238: // don't synchronize it, the more random the better.
1239: return databaseEncrypted ? random.nextInt() : 0;
1240: }
1241:
1242: public Serializable changeBootPassword(Properties properties,
1243: Serializable changePassword) throws StandardException {
1244: if (isReadOnly())
1245: throw StandardException
1246: .newException(SQLState.DATABASE_READ_ONLY);
1247:
1248: if (!databaseEncrypted)
1249: throw StandardException
1250: .newException(SQLState.DATABASE_NOT_ENCRYPTED);
1251:
1252: if (changePassword == null)
1253: throw StandardException
1254: .newException(SQLState.NULL_BOOT_PASSWORD);
1255:
1256: if (!(changePassword instanceof String))
1257: throw StandardException
1258: .newException(SQLState.NON_STRING_BP);
1259:
1260: // the new bootPassword is expected to be of the form
1261: // oldkey , newkey.
1262: String changeString = (String) changePassword;
1263:
1264: return currentCipherFactory.changeBootPassword(
1265: (String) changePassword, properties, encryptionEngine);
1266:
1267: }
1268:
1269: /**
1270: * (re) encryption testing debug flags that are used to
1271: * simulate error/crash conditions for testing purposes.
1272: * When any one of the following flags are set to true
1273: * in the debug mode, re-encryption will fail at that point.
1274: */
1275:
1276: public static final String TEST_REENCRYPT_CRASH_BEFORE_COMMT = SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_BEFORE_COMMT"
1277: : null;
1278: public static final String TEST_REENCRYPT_CRASH_AFTER_COMMT = SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_COMMT"
1279: : null;
1280: public static final String TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY = SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY"
1281: : null;
1282: public static final String TEST_REENCRYPT_CRASH_AFTER_CHECKPOINT = SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_CHECKPOINT"
1283: : null;
1284: public static final String TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE = SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE"
1285: : null;
1286: public static final String TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY = SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY"
1287: : null;
1288: public static final String TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP = SanityManager.DEBUG ? "TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP"
1289: : null;
1290:
1291: /**
1292: * when the input debug flag is set, an expception
1293: * is throw when run in the debug mode.
1294: */
1295: private void crashOnDebugFlag(String debugFlag, boolean reEncrypt)
1296: throws StandardException {
1297: if (SanityManager.DEBUG) {
1298: // if the test debug flag is set, throw an
1299: // exception to simulate error cases.
1300: if (SanityManager.DEBUG_ON(debugFlag)) {
1301: StandardException se = StandardException
1302: .newException(
1303: (reEncrypt ? SQLState.DATABASE_REENCRYPTION_FAILED
1304: : SQLState.DATABASE_ENCRYPTION_FAILED),
1305: debugFlag);
1306: markCorrupt(se);
1307: throw se;
1308: }
1309: }
1310: }
1311:
1312: /*
1313: * Configure the database for encryption, with the specified
1314: * encryption properties.
1315: *
1316: * Basic idea is to encrypt all the containers with new password/key
1317: * specified by the user and keep old versions of the data to
1318: * rollback the database to the state before the configuration of database
1319: * with new encryption attributes. Users can configure the database with
1320: * new encryption attributes at boot time only; advantage of this approach
1321: * is that there will not be any concurrency issues to handle because
1322: * no users will be modifying the data.
1323: *
1324: * First step is to encrypt the existing data with new encryption
1325: * attributes and then update the encryption properties for
1326: * the database. Configuring an un-encrypted database for
1327: * encryption problem is a minor variation of re-encrypting an
1328: * encrypted database with new encryption key. The database
1329: * reconfiguration with new encryption attributes is done under one
1330: * transaction, if there is a crash/error before it is committed,
1331: * then it is rolled back and the database will be brought back to the
1332: * state it was before the encryption.
1333: *
1334: * One trickey case in (re) encrypion of database is
1335: * unlike standard protocol transaction commit means all done,
1336: * database (re) encryption process has to perform a checkpoint
1337: * with a newly generated key then only database (re) encrption
1338: * is complete, Otherwise the problem is recovery has to deal
1339: * with transaction log that is encrypted with old encryption key and
1340: * the new encryption key. This probelm is avoided writing COMMIT
1341: * and new CHECKPOINT log record to a new log file and encrypt the
1342: * with a new key, if there is crash before checkpoint records
1343: * are updated , then on next boot the log file after the checkpoint
1344: * is deleted before reovery, which will be the one that is
1345: * written with new encryption key and also contains COMMIT record,
1346: * so the COMMIT record is also gone when log file is deleted.
1347: * Recovery will not see the commit , so it will rollback the (re)
1348: * encryption and revert all the containers to the
1349: * original versions.
1350: *
1351: * Old container versions are deleted only when the check point
1352: * with new encryption key is successful, not on post-commit.
1353: *
1354: * @param properties properties related to this database.
1355: * @exception StandardException Standard Derby Error Policy
1356: */
1357: public void configureDatabaseForEncryption(Properties properties,
1358: CipherFactory newCipherFactory) throws StandardException {
1359:
1360: boolean reEncrypt = (databaseEncrypted && encryptDatabase);
1361:
1362: // check if the database can be encrypted.
1363: canEncryptDatabase(reEncrypt);
1364:
1365: boolean externalKeyEncryption = false;
1366: if (properties.getProperty(Attribute.CRYPTO_EXTERNAL_KEY) != null) {
1367: externalKeyEncryption = true;
1368: }
1369:
1370: // check point the datase, so that encryption does not have
1371: // to encrypt the existing transactions logs.
1372:
1373: logFactory.checkpoint(this , dataFactory, xactFactory, true);
1374:
1375: // start a transaction that is to be used for encryting the database
1376: RawTransaction transaction = xactFactory.startTransaction(this ,
1377: ContextService.getFactory().getCurrentContextManager(),
1378: AccessFactoryGlobals.USER_TRANS_NAME);
1379:
1380: try {
1381:
1382: dataFactory.encryptAllContainers(transaction);
1383:
1384: // all the containers are (re) encrypted, now mark the database as
1385: // encrypted if a plain database is getting configured for encryption
1386: // or update the encryption the properties, in the
1387: // service.properties ..etc.
1388:
1389: if (SanityManager.DEBUG) {
1390: crashOnDebugFlag(TEST_REENCRYPT_CRASH_BEFORE_COMMT,
1391: reEncrypt);
1392: }
1393:
1394: // check if the checkpoint is currently in the last log file,
1395: // otherwise force a checkpoint and then do a log switch,
1396: // after setting up a new encryption key
1397: if (!logFactory.isCheckpointInLastLogFile()) {
1398: // perfrom a checkpoint, this is a reference checkpoint
1399: // to find if the re(encryption) is complete.
1400: logFactory.checkpoint(this , dataFactory, xactFactory,
1401: true);
1402: }
1403:
1404: encryptDatabase = false;
1405:
1406: // let the log factory know that database is
1407: // (re) encrypted and ask it to flush the log,
1408: // before enabling encryption of the log with
1409: // the new key.
1410: logFactory.setDatabaseEncrypted(true);
1411:
1412: // let the log factory and data factory know that
1413: // database is encrypted.
1414: if (!reEncrypt) {
1415: // mark in the raw store that the database is
1416: // encrypted.
1417: databaseEncrypted = true;
1418: dataFactory.setDatabaseEncrypted();
1419: } else {
1420: // switch the encryption/decryption engine to the new ones.
1421: decryptionEngine = newDecryptionEngine;
1422: encryptionEngine = newEncryptionEngine;
1423: currentCipherFactory = newCipherFactory;
1424: }
1425:
1426: // make the log factory ready to encrypt
1427: // the transaction log with the new encryption
1428: // key by switching to a new log file.
1429: // If re-encryption is aborted for any reason,
1430: // this new log file will be deleted, during
1431: // recovery.
1432:
1433: logFactory.startNewLogFile();
1434:
1435: // mark that re-encryption is in progress in the
1436: // service.properties, so that (re) encryption
1437: // changes that can not be undone using the transaction
1438: // log can be un-done before recovery starts.
1439: // (like the changes to service.properties and
1440: // any log files the can not be understood by the
1441: // old encryption key), incase engine crashes
1442: // after this point.
1443:
1444: // if the crash occurs before this point, recovery
1445: // will rollback the changes using the transaction
1446: // log.
1447:
1448: properties
1449: .put(
1450: RawStoreFactory.DB_ENCRYPTION_STATUS,
1451: String
1452: .valueOf(RawStoreFactory.DB_ENCRYPTION_IN_PROGRESS));
1453:
1454: if (reEncrypt) {
1455: // incase re-encryption, save the old
1456: // encryption related properties, before
1457: // doing updates with new values.
1458:
1459: if (externalKeyEncryption) {
1460: // save the current copy of verify key file.
1461: StorageFile verifyKeyFile = storageFactory
1462: .newStorageFile(Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE);
1463: StorageFile oldVerifyKeyFile = storageFactory
1464: .newStorageFile(RawStoreFactory.CRYPTO_OLD_EXTERNAL_KEY_VERIFY_FILE);
1465:
1466: if (!privCopyFile(verifyKeyFile, oldVerifyKeyFile))
1467: throw StandardException.newException(
1468: SQLState.RAWSTORE_ERROR_COPYING_FILE,
1469: verifyKeyFile, oldVerifyKeyFile);
1470:
1471: // update the verify key file with the new key info.
1472: currentCipherFactory.verifyKey(reEncrypt,
1473: storageFactory, properties);
1474: } else {
1475: // save the current generated encryption key
1476: String keyString = properties
1477: .getProperty(RawStoreFactory.ENCRYPTED_KEY);
1478: if (keyString != null)
1479: properties.put(
1480: RawStoreFactory.OLD_ENCRYPTED_KEY,
1481: keyString);
1482: }
1483: } else {
1484: // save the encryption block size;
1485: properties.put(RawStoreFactory.ENCRYPTION_BLOCKSIZE,
1486: String.valueOf(encryptionBlockSize));
1487: }
1488:
1489: // save the new encryption properties into service.properties
1490: currentCipherFactory.saveProperties(properties);
1491:
1492: if (SanityManager.DEBUG) {
1493: crashOnDebugFlag(
1494: TEST_REENCRYPT_CRASH_AFTER_SWITCH_TO_NEWKEY,
1495: reEncrypt);
1496: }
1497:
1498: // commit the transaction that is used to
1499: // (re) encrypt the database. Note that
1500: // this will be logged with newly generated
1501: // encryption key in the new log file created
1502: // above.
1503: transaction.commit();
1504:
1505: if (SanityManager.DEBUG) {
1506: crashOnDebugFlag(TEST_REENCRYPT_CRASH_AFTER_COMMT,
1507: reEncrypt);
1508: }
1509:
1510: // force the checkpoint with new encryption key.
1511: logFactory.checkpoint(this , dataFactory, xactFactory, true);
1512:
1513: if (SanityManager.DEBUG) {
1514: crashOnDebugFlag(TEST_REENCRYPT_CRASH_AFTER_CHECKPOINT,
1515: reEncrypt);
1516: }
1517:
1518: // once the checkpont makes it to the log, re-encrption
1519: // is complete. only cleanup is remaining ; update the
1520: // re-encryption status flag to cleanup.
1521: properties.put(RawStoreFactory.DB_ENCRYPTION_STATUS, String
1522: .valueOf(RawStoreFactory.DB_ENCRYPTION_IN_CLEANUP));
1523:
1524: // database is (re)encrypted successfuly,
1525: // remove the old version of the container files.
1526: dataFactory.removeOldVersionOfContainers(false);
1527:
1528: if (reEncrypt) {
1529: if (externalKeyEncryption) {
1530: // remove the saved copy of the verify.key file
1531: StorageFile oldVerifyKeyFile = storageFactory
1532: .newStorageFile(RawStoreFactory.CRYPTO_OLD_EXTERNAL_KEY_VERIFY_FILE);
1533: if (!privDelete(oldVerifyKeyFile))
1534: throw StandardException.newException(
1535: SQLState.UNABLE_TO_DELETE_FILE,
1536: oldVerifyKeyFile);
1537: } else {
1538: // remove the old encryption key property.
1539: properties
1540: .remove(RawStoreFactory.OLD_ENCRYPTED_KEY);
1541: }
1542: }
1543:
1544: // (re) encrypion is done, remove the (re)
1545: // encryption status property.
1546:
1547: properties.remove(RawStoreFactory.DB_ENCRYPTION_STATUS);
1548:
1549: // close the transaction.
1550: transaction.close();
1551:
1552: } catch (StandardException se) {
1553:
1554: throw StandardException.newException(
1555: (reEncrypt ? SQLState.DATABASE_REENCRYPTION_FAILED
1556: : SQLState.DATABASE_ENCRYPTION_FAILED), se,
1557: se.getMessage());
1558: } finally {
1559: // clear the new encryption engines.
1560: newDecryptionEngine = null;
1561: newEncryptionEngine = null;
1562: }
1563: }
1564:
1565: /**
1566: * Engine might have crashed during encryption of un-encrypted datbase
1567: * or while re-encryptin an already encrypted database with a new key
1568: * after all the containers or (re) encrypted. If crash has occured
1569: * before all containers are encrypted, recovery wil un-do re-encryption
1570: * using the transaction log, nothing to be done here.
1571: *
1572: * If crash has occured after database encryption status flag
1573: * (RawStoreFactory.DB_ENCRYPTION_STATUS) is set, this method
1574: * will do any cleanup necessary for the recovery to correctly
1575: * perform the rollback if required.
1576: *
1577: *
1578: *
1579: * @param properties properties related to this database.
1580: * @exception StandardException Standard Derby Error Policy
1581: *
1582: */
1583: public void handleIncompleteDatabaseEncryption(Properties properties)
1584: throws StandardException {
1585: // find what was the encryption status before database crashed.
1586: int dbEncryptionStatus = 0;
1587: String dbEncryptionStatusStr = properties
1588: .getProperty(RawStoreFactory.DB_ENCRYPTION_STATUS);
1589: if (dbEncryptionStatusStr != null)
1590: dbEncryptionStatus = Integer
1591: .parseInt(dbEncryptionStatusStr);
1592:
1593: boolean reEncryption = false;
1594: // check if engine crashed when (re) encryption was in progress.
1595: if (dbEncryptionStatus == RawStoreFactory.DB_ENCRYPTION_IN_PROGRESS) {
1596:
1597: // check if it crashed immediately after completion or
1598: // before. if the checkpoint is in the last log file
1599: // encrypted with new encryption key, it is as good
1600: // as complete. In this case just cleanup any uncleared
1601: // flags and mark that database is encrypted.
1602:
1603: if (logFactory.isCheckpointInLastLogFile()) {
1604: // database (re)encryption was successful, only
1605: // cleanup is remaining. change the status to cleanup.
1606: dbEncryptionStatus = RawStoreFactory.DB_ENCRYPTION_IN_CLEANUP;
1607: } else {
1608:
1609: // crash occured before re-encrytion was completed.
1610: // update the db re-encryption status and write to
1611: // the service.properties that re-encryption
1612: // needs to be undone. The reason this status need
1613: // to be made persistent, it will help to correctly
1614: // handle a crash in this routine after the log file
1615: // encrypted with new key is deleted. If this flag
1616: // is not set, on next reboot, above check
1617: // will find checkpoint in the last log file and
1618: // incorrecly assume (re) encryption is
1619: // successful.
1620:
1621: dbEncryptionStatus = RawStoreFactory.DB_ENCRYPTION_IN_UNDO;
1622: properties.put(RawStoreFactory.DB_ENCRYPTION_STATUS,
1623: String.valueOf(dbEncryptionStatus));
1624: }
1625: }
1626:
1627: if (dbEncryptionStatus == RawStoreFactory.DB_ENCRYPTION_IN_UNDO) {
1628: // delete the log file after the log file that has the checkpoint ,
1629: // it has the data encrypted with the new key, including the commit
1630: // record for the transaction that was used to (re)encrypt
1631: // the database. By Deleting the log file, we are forcing the
1632: // recovery to rollback the (re)encryption of the database.
1633:
1634: logFactory.deleteLogFileAfterCheckpointLogFile();
1635:
1636: if (SanityManager.DEBUG) {
1637: crashOnDebugFlag(
1638: TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_LOGFILE_DELETE,
1639: reEncryption);
1640: }
1641:
1642: // Note : If a crash occurs at this point, then on reboot
1643: // it will again be in the DB_ENRYPTION_IN__UNDO state,
1644: // there will not be a file after the checkpoint log file,
1645: // so no file will be deleted.
1646:
1647: // check if this is a external key encryption and
1648: // if it replace the current verify key file with
1649: // the old copy.
1650:
1651: StorageFile verifyKeyFile = storageFactory
1652: .newStorageFile(Attribute.CRYPTO_EXTERNAL_KEY_VERIFY_FILE);
1653:
1654: if (privExists(verifyKeyFile)) {
1655: StorageFile oldVerifyKeyFile = storageFactory
1656: .newStorageFile(RawStoreFactory.CRYPTO_OLD_EXTERNAL_KEY_VERIFY_FILE);
1657:
1658: if (privExists(oldVerifyKeyFile)) {
1659: if (!privCopyFile(oldVerifyKeyFile, verifyKeyFile))
1660: throw StandardException.newException(
1661: SQLState.RAWSTORE_ERROR_COPYING_FILE,
1662: oldVerifyKeyFile, verifyKeyFile);
1663:
1664: // only incase of re-encryption there should
1665: // be old verify key file.
1666: reEncryption = true;
1667: } else {
1668: // remove the verify key file.
1669: if (!privDelete(verifyKeyFile))
1670: throw StandardException.newException(
1671: SQLState.UNABLE_TO_DELETE_FILE,
1672: verifyKeyFile);
1673: }
1674:
1675: } else {
1676: // database enrypted with boot password.
1677:
1678: // replace the current encryption key with the old key
1679: // in the service.properties file.
1680: // retreive the old encryption key
1681:
1682: String OldKeyString = properties
1683: .getProperty(RawStoreFactory.OLD_ENCRYPTED_KEY);
1684:
1685: if (OldKeyString != null) {
1686: // set the current encrypted key to the old one.
1687: properties.put(RawStoreFactory.ENCRYPTED_KEY,
1688: OldKeyString);
1689:
1690: // only incase of re-encryption there should
1691: // be old encryted key .
1692: reEncryption = true;
1693: }
1694: }
1695:
1696: if (!reEncryption) {
1697: // crash occured when database was getting reconfigured
1698: // for encryption , all encryption properties should be
1699: // removed from service.properties
1700:
1701: // common props for external key or password.
1702: properties.remove(Attribute.DATA_ENCRYPTION);
1703: properties
1704: .remove(RawStoreFactory.LOG_ENCRYPT_ALGORITHM_VERSION);
1705: properties
1706: .remove(RawStoreFactory.DATA_ENCRYPT_ALGORITHM_VERSION);
1707: properties.remove(RawStoreFactory.ENCRYPTION_BLOCKSIZE);
1708:
1709: // properties specific to password based encryption.
1710: properties.remove(Attribute.CRYPTO_KEY_LENGTH);
1711: properties.remove(Attribute.CRYPTO_PROVIDER);
1712: properties.remove(Attribute.CRYPTO_ALGORITHM);
1713: properties.remove(RawStoreFactory.ENCRYPTED_KEY);
1714:
1715: }
1716:
1717: if (SanityManager.DEBUG) {
1718: crashOnDebugFlag(
1719: TEST_REENCRYPT_CRASH_AFTER_RECOVERY_UNDO_REVERTING_KEY,
1720: reEncryption);
1721: }
1722:
1723: } // end of UNDO
1724:
1725: if (dbEncryptionStatus == RawStoreFactory.DB_ENCRYPTION_IN_CLEANUP) {
1726: // remove all the old versions of the containers.
1727: dataFactory.removeOldVersionOfContainers(true);
1728: }
1729:
1730: if (SanityManager.DEBUG) {
1731: crashOnDebugFlag(
1732: TEST_REENCRYPT_CRASH_BEFORE_RECOVERY_FINAL_CLEANUP,
1733: reEncryption);
1734: }
1735:
1736: // either the (re) encryption was complete ,
1737: // or undone (except for rollback that needs to be
1738: // done by the recovery). Remove re-encryption specific
1739: // flags from the service.properties and old copy
1740: // of the verify key file.
1741:
1742: // delete the old verify key file , if it exists.
1743: StorageFile oldVerifyKeyFile = storageFactory
1744: .newStorageFile(RawStoreFactory.CRYPTO_OLD_EXTERNAL_KEY_VERIFY_FILE);
1745: if (privExists(oldVerifyKeyFile)) {
1746: if (!privDelete(oldVerifyKeyFile))
1747: throw StandardException.newException(
1748: SQLState.UNABLE_TO_DELETE_FILE,
1749: oldVerifyKeyFile);
1750: } else {
1751: // remove the old encryption key property.
1752: properties.remove(RawStoreFactory.OLD_ENCRYPTED_KEY);
1753: }
1754:
1755: // remove the re-encryptin status flag.
1756: properties.remove(RawStoreFactory.DB_ENCRYPTION_STATUS);
1757: }
1758:
1759: /**
1760: * checks if the database is in the right state to (re)encrypt it.
1761: *
1762: * @param reEncrypt true if the database getting encrypted
1763: * with new password/key.
1764: * @exception StandardException
1765: * if there is global transaction in the prepared state or
1766: * if the database is not at the version 10.2 or above, this
1767: * feature is not supported or
1768: * if the log is archived for the database.
1769: */
1770: private void canEncryptDatabase(boolean reEncrypt)
1771: throws StandardException {
1772:
1773: String feature = (reEncrypt ? "newBootPassword/newEncryptionKey attribute"
1774: : "dataEncryption attribute on an existing database");
1775:
1776: // check if the database version is at 10.2 or above.
1777: // encrytpion or re-encryption of the database
1778: // is supported only in version 10.2 or above.
1779: logFactory.checkVersion(
1780: RawStoreFactory.DERBY_STORE_MAJOR_VERSION_10,
1781: RawStoreFactory.DERBY_STORE_MINOR_VERSION_2, feature);
1782:
1783: // database can not be (re)encrypted if there
1784: // are any global transactions in the prepared state
1785: // after the recovery. The reason for this restriction
1786: // is that any transaction log before the encryption can not
1787: // be read once database is reconfigure with new encryption
1788: // key.
1789: if (xactFactory.hasPreparedXact()) {
1790: if (reEncrypt)
1791: throw StandardException
1792: .newException(SQLState.REENCRYPTION_PREPARED_XACT_EXIST);
1793: else
1794: throw StandardException
1795: .newException(SQLState.ENCRYPTION_PREPARED_XACT_EXIST);
1796: }
1797:
1798: // check if the database has the log archived.
1799: // database can not be congured of encryption or
1800: // or re-encrypt it with a new key when the database
1801: // log is being archived. The reason for this restriction is
1802: // it will create a scenarion where users will
1803: // have some logs encrypted with new key and some with old key
1804: // when rollforward recovery is performed.
1805:
1806: if (logFactory.logArchived()) {
1807: if (reEncrypt)
1808: throw StandardException
1809: .newException(SQLState.CANNOT_REENCRYPT_LOG_ARCHIVED_DATABASE);
1810: else
1811: throw StandardException
1812: .newException(SQLState.CANNOT_ENCRYPT_LOG_ARCHIVED_DATABASE);
1813:
1814: }
1815: }
1816:
1817: /*
1818: **
1819: */
1820:
1821: public StandardException markCorrupt(StandardException originalError) {
1822:
1823: logFactory.markCorrupt(originalError);
1824: dataFactory.markCorrupt(originalError);
1825: xactFactory.markCorrupt(originalError);
1826:
1827: return originalError;
1828: }
1829:
1830: /*
1831: * class specific methods
1832: */
1833:
1834: /* subclass can override this method to load different submodules */
1835: public String getTransactionFactoryModule() {
1836: return TransactionFactory.MODULE;
1837: }
1838:
1839: public String getDataFactoryModule() {
1840: return DataFactory.MODULE;
1841: }
1842:
1843: public String getLogFactoryModule() {
1844: return LogFactory.MODULE;
1845: }
1846:
1847: private void logHistory(OutputStreamWriter historyFile, String msg)
1848: throws IOException {
1849: Date d = new Date();
1850: historyFile.write(d.toString() + ":" + msg + "\n");
1851: historyFile.flush();
1852: }
1853:
1854: /*
1855: * Get the file path. If the canonical path can be obtained then return the
1856: * canonical path, otherwise just return the abstract path. Typically if
1857: * there are no permission to read user.dir when running under security
1858: * manager canonical path can not be obtained.
1859: *
1860: * This method is used to a write path name to error/status log file, where it
1861: * would be nice to print full paths but not esstential that the user
1862: * grant permissions to read user.dir property.
1863: */
1864: private String getFilePath(StorageFile file) {
1865: String path = privGetCanonicalPath(file);
1866: if (path != null) {
1867: return path;
1868: } else {
1869: //can not get the canoncal path,
1870: // return the abstract path
1871: return file.getPath();
1872: }
1873: }
1874:
1875: /*
1876: * Get the file path. If the canonical path can be obtained then return the
1877: * canonical path, otherwise just return the abstract path. Typically if
1878: * there are no permission to read user.dir when running under security
1879: * manager canonical path can not be obtained.
1880: *
1881: * This method is used to a write a file path name to error/status log file,
1882: * where it would be nice to print full paths but not esstential that the user
1883: * grant permissions to read user.dir property.
1884: *
1885: */
1886: private String getFilePath(File file) {
1887: String path = privGetCanonicalPath(file);
1888: if (path != null) {
1889: return path;
1890: } else {
1891: // can not get the canoncal path,
1892: // return the abstract path
1893: return file.getPath();
1894: }
1895: }
1896:
1897: protected boolean privCopyDirectory(StorageFile from, File to) {
1898: return privCopyDirectory(from, to, (byte[]) null,
1899: (String[]) null, true);
1900: }
1901:
1902: protected boolean privCopyDirectory(File from, StorageFile to) {
1903: return privCopyDirectory(from, to, (byte[]) null,
1904: (String[]) null);
1905: }
1906:
1907: /**
1908: * Return an id which can be used to create a container.
1909: * <p>
1910: * Return an id number with is greater than any existing container
1911: * in the current database. Caller will use this to allocate future
1912: * container numbers - most likely caching the value and then incrementing
1913: * it as it is used.
1914: * <p>
1915: *
1916: * @return The an id which can be used to create a container.
1917: *
1918: * @exception StandardException Standard exception policy.
1919: **/
1920: public long getMaxContainerId() throws StandardException {
1921: return (dataFactory.getMaxContainerId());
1922: }
1923:
1924: /*
1925: These methods require Priv Blocks when run under a security manager.
1926: */
1927:
1928: private synchronized OutputStreamWriter privFileWriter(
1929: StorageFile fileName, boolean append) throws IOException {
1930: actionCode = FILE_WRITER_ACTION;
1931: actionStorageFile = fileName;
1932: actionAppend = append;
1933: try {
1934: return (OutputStreamWriter) java.security.AccessController
1935: .doPrivileged(this );
1936: } catch (java.security.PrivilegedActionException pae) {
1937: throw (IOException) pae.getException();
1938: } finally {
1939: actionStorageFile = null;
1940: }
1941: }
1942:
1943: private synchronized boolean privExists(File file) {
1944: actionCode = REGULAR_FILE_EXISTS_ACTION;
1945: actionRegularFile = file;
1946:
1947: try {
1948: Object ret = AccessController.doPrivileged(this );
1949: return ((Boolean) ret).booleanValue();
1950: } catch (PrivilegedActionException pae) {
1951: return false;
1952: } // does not throw an exception
1953: finally {
1954: actionRegularFile = null;
1955: }
1956: }
1957:
1958: private synchronized boolean privExists(final StorageFile file) {
1959: actionCode = STORAGE_FILE_EXISTS_ACTION;
1960: actionStorageFile = file;
1961:
1962: try {
1963: Object ret = AccessController.doPrivileged(this );
1964: return ((Boolean) ret).booleanValue();
1965: } catch (PrivilegedActionException pae) {
1966: return false;
1967: } // does not throw an exception
1968: finally {
1969: actionStorageFile = null;
1970: }
1971: }
1972:
1973: private synchronized boolean privDelete(File file) {
1974: actionCode = REGULAR_FILE_DELETE_ACTION;
1975: actionRegularFile = file;
1976:
1977: try {
1978: Object ret = AccessController.doPrivileged(this );
1979: return ((Boolean) ret).booleanValue();
1980: } catch (PrivilegedActionException pae) {
1981: return false;
1982: } // does not throw an exception
1983: finally {
1984: actionRegularFile = null;
1985: }
1986: }
1987:
1988: private synchronized boolean privDelete(StorageFile file) {
1989: actionCode = STORAGE_FILE_DELETE_ACTION;
1990: actionStorageFile = file;
1991:
1992: try {
1993: Object ret = AccessController.doPrivileged(this );
1994: return ((Boolean) ret).booleanValue();
1995: } catch (PrivilegedActionException pae) {
1996: return false;
1997: } // does not throw an exception
1998: finally {
1999: actionStorageFile = null;
2000: }
2001: }
2002:
2003: private synchronized boolean privMkdirs(File file) {
2004: actionCode = REGULAR_FILE_MKDIRS_ACTION;
2005: actionRegularFile = file;
2006:
2007: try {
2008: Object ret = AccessController.doPrivileged(this );
2009: return ((Boolean) ret).booleanValue();
2010: } catch (PrivilegedActionException pae) {
2011: return false;
2012: } // does not throw an exception
2013: finally {
2014: actionRegularFile = null;
2015: }
2016: }
2017:
2018: private synchronized boolean privIsDirectory(File file) {
2019: actionCode = REGULAR_FILE_IS_DIRECTORY_ACTION;
2020: actionRegularFile = file;
2021:
2022: try {
2023: Object ret = AccessController.doPrivileged(this );
2024: return ((Boolean) ret).booleanValue();
2025: } catch (PrivilegedActionException pae) {
2026: return false;
2027: } // does not throw an exception
2028: finally {
2029: actionRegularFile = null;
2030: }
2031: }
2032:
2033: private synchronized boolean privRemoveDirectory(File file) {
2034: actionCode = REGULAR_FILE_REMOVE_DIRECTORY_ACTION;
2035: actionRegularFile = file;
2036:
2037: try {
2038: Object ret = AccessController.doPrivileged(this );
2039: return ((Boolean) ret).booleanValue();
2040: } catch (PrivilegedActionException pae) {
2041: return false;
2042: } // does not throw an exception
2043: finally {
2044: actionRegularFile = null;
2045: }
2046: }
2047:
2048: private synchronized boolean privRenameTo(File file1, File file2) {
2049: actionCode = REGULAR_FILE_RENAME_TO_ACTION;
2050: actionRegularFile = file1;
2051: actionRegularFile2 = file2;
2052:
2053: try {
2054: Object ret = AccessController.doPrivileged(this );
2055: return ((Boolean) ret).booleanValue();
2056: } catch (PrivilegedActionException pae) {
2057: return false;
2058: } // does not throw an exception
2059: finally {
2060: actionRegularFile = null;
2061: actionRegularFile2 = null;
2062: }
2063: }
2064:
2065: private synchronized boolean privCopyDirectory(StorageFile from,
2066: File to, byte[] buffer, String[] filter, boolean copySubdirs) {
2067: actionCode = COPY_STORAGE_DIRECTORY_TO_REGULAR_ACTION;
2068: actionStorageFile = from;
2069: actionRegularFile = to;
2070: actionBuffer = buffer;
2071: actionFilter = filter;
2072: actionCopySubDirs = copySubdirs;
2073:
2074: try {
2075: Object ret = AccessController.doPrivileged(this );
2076: return ((Boolean) ret).booleanValue();
2077: } catch (PrivilegedActionException pae) {
2078: return false;
2079: } // does not throw an exception
2080: finally {
2081: actionStorageFile = null;
2082: actionRegularFile = null;
2083: actionBuffer = null;
2084: actionFilter = null;
2085: }
2086: }
2087:
2088: private synchronized boolean privCopyDirectory(File from,
2089: StorageFile to, byte[] buffer, String[] filter) {
2090: actionCode = COPY_REGULAR_DIRECTORY_TO_STORAGE_ACTION;
2091: actionStorageFile = to;
2092: actionRegularFile = from;
2093: actionBuffer = buffer;
2094: actionFilter = filter;
2095:
2096: try {
2097: Object ret = AccessController.doPrivileged(this );
2098: return ((Boolean) ret).booleanValue();
2099: } catch (PrivilegedActionException pae) {
2100: return false;
2101: } // does not throw an exception
2102: finally {
2103: actionStorageFile = null;
2104: actionRegularFile = null;
2105: actionBuffer = null;
2106: actionFilter = null;
2107: }
2108: }
2109:
2110: private synchronized boolean privCopyFile(File from, StorageFile to) {
2111: actionCode = COPY_REGULAR_FILE_TO_STORAGE_ACTION;
2112: actionStorageFile = to;
2113: actionRegularFile = from;
2114:
2115: try {
2116: Object ret = AccessController.doPrivileged(this );
2117: return ((Boolean) ret).booleanValue();
2118: } catch (PrivilegedActionException pae) {
2119: return false;
2120: } // does not throw an exception
2121: finally {
2122: actionStorageFile = null;
2123: actionRegularFile = null;
2124: }
2125: }
2126:
2127: private synchronized boolean privCopyFile(StorageFile from, File to) {
2128: actionCode = COPY_STORAGE_FILE_TO_REGULAR_ACTION;
2129: actionStorageFile = from;
2130: actionRegularFile = to;
2131:
2132: try {
2133: Object ret = AccessController.doPrivileged(this );
2134: return ((Boolean) ret).booleanValue();
2135: } catch (PrivilegedActionException pae) {
2136: return false;
2137: } // does not throw an exception
2138: finally {
2139: actionStorageFile = null;
2140: actionRegularFile = null;
2141: }
2142: }
2143:
2144: private synchronized boolean privCopyFile(StorageFile from,
2145: StorageFile to) {
2146: actionCode = COPY_STORAGE_FILE_TO_STORAGE_ACTION;
2147: actionStorageFile = from;
2148: actionToStorageFile = to;
2149:
2150: try {
2151: Object ret = AccessController.doPrivileged(this );
2152: return ((Boolean) ret).booleanValue();
2153: } catch (PrivilegedActionException pae) {
2154: return false;
2155: } // does not throw an exception
2156: finally {
2157: actionStorageFile = null;
2158: actionToStorageFile = null;
2159: }
2160: }
2161:
2162: private synchronized String[] privList(final File file) {
2163: actionCode = REGULAR_FILE_LIST_DIRECTORY_ACTION;
2164: actionRegularFile = file;
2165:
2166: try {
2167: return (String[]) AccessController.doPrivileged(this );
2168: } catch (PrivilegedActionException pae) {
2169: return null;
2170: } // does not throw an exception
2171: finally {
2172: actionRegularFile = null;
2173: }
2174: }
2175:
2176: private synchronized String[] privList(final StorageFile file) {
2177: actionCode = STORAGE_FILE_LIST_DIRECTORY_ACTION;
2178: actionStorageFile = file;
2179:
2180: try {
2181: return (String[]) AccessController.doPrivileged(this );
2182: } catch (PrivilegedActionException pae) {
2183: return null;
2184: } // does not throw an exception
2185: finally {
2186: actionStorageFile = null;
2187: }
2188: }
2189:
2190: private synchronized String privGetCanonicalPath(
2191: final StorageFile file) {
2192: actionCode = STORAGE_FILE_GET_CANONICALPATH_ACTION;
2193: actionStorageFile = file;
2194:
2195: try {
2196: return (String) AccessController.doPrivileged(this );
2197: } catch (PrivilegedActionException pae) {
2198: return null;
2199: } // does not throw an exception
2200: catch (SecurityException se) {
2201: // there are no permission to get canonical path
2202: // just return null.
2203: return null;
2204: } finally {
2205: actionStorageFile = null;
2206: }
2207: }
2208:
2209: private synchronized String privGetCanonicalPath(final File file) {
2210: actionCode = REGULAR_FILE_GET_CANONICALPATH_ACTION;
2211: actionRegularFile = file;
2212:
2213: try {
2214: return (String) AccessController.doPrivileged(this );
2215: } catch (PrivilegedActionException pae) {
2216: return null;
2217: } // does not throw an exception
2218: catch (SecurityException se) {
2219: // there are no permission to get canonical path
2220: // just return null.
2221: return null;
2222: } finally {
2223: actionRegularFile = null;
2224: }
2225: }
2226:
2227: // PrivilegedExceptionAction method
2228: public final Object run() throws IOException {
2229: switch (actionCode) {
2230: case FILE_WRITER_ACTION:
2231: // SECURITY PERMISSION - MP1
2232: return new OutputStreamWriter(actionStorageFile
2233: .getOutputStream(actionAppend));
2234:
2235: case REGULAR_FILE_EXISTS_ACTION:
2236: return ReuseFactory.getBoolean(actionRegularFile.exists());
2237:
2238: case STORAGE_FILE_EXISTS_ACTION:
2239: return ReuseFactory.getBoolean(actionStorageFile.exists());
2240:
2241: case REGULAR_FILE_DELETE_ACTION:
2242: return ReuseFactory.getBoolean(actionRegularFile.delete());
2243:
2244: case STORAGE_FILE_DELETE_ACTION:
2245: return ReuseFactory.getBoolean(actionStorageFile.delete());
2246:
2247: case REGULAR_FILE_MKDIRS_ACTION:
2248: // SECURITY PERMISSION - OP4
2249: return ReuseFactory.getBoolean(actionRegularFile.mkdirs());
2250:
2251: case REGULAR_FILE_IS_DIRECTORY_ACTION:
2252: // SECURITY PERMISSION - MP1
2253: return ReuseFactory.getBoolean(actionRegularFile
2254: .isDirectory());
2255:
2256: case REGULAR_FILE_REMOVE_DIRECTORY_ACTION:
2257: // SECURITY PERMISSION - MP1, OP5
2258: return ReuseFactory.getBoolean(FileUtil
2259: .removeDirectory(actionRegularFile));
2260:
2261: case REGULAR_FILE_RENAME_TO_ACTION:
2262: // SECURITY PERMISSION - OP4
2263: return ReuseFactory.getBoolean(actionRegularFile
2264: .renameTo(actionRegularFile2));
2265:
2266: case COPY_STORAGE_DIRECTORY_TO_REGULAR_ACTION:
2267: // SECURITY PERMISSION - MP1, OP4
2268: return ReuseFactory.getBoolean(FileUtil.copyDirectory(
2269: storageFactory, actionStorageFile,
2270: actionRegularFile, actionBuffer, actionFilter,
2271: actionCopySubDirs));
2272:
2273: case COPY_REGULAR_DIRECTORY_TO_STORAGE_ACTION:
2274: // SECURITY PERMISSION - MP1, OP4
2275: return ReuseFactory.getBoolean(FileUtil.copyDirectory(
2276: (WritableStorageFactory) storageFactory,
2277: actionRegularFile, actionStorageFile, actionBuffer,
2278: actionFilter));
2279:
2280: case COPY_REGULAR_FILE_TO_STORAGE_ACTION:
2281: // SECURITY PERMISSION - MP1, OP4
2282: return ReuseFactory.getBoolean(FileUtil.copyFile(
2283: (WritableStorageFactory) storageFactory,
2284: actionRegularFile, actionStorageFile));
2285:
2286: case REGULAR_FILE_LIST_DIRECTORY_ACTION:
2287: // SECURITY PERMISSION - MP1
2288: return (String[]) (actionRegularFile.list());
2289:
2290: case STORAGE_FILE_LIST_DIRECTORY_ACTION:
2291: // SECURITY PERMISSION - MP1
2292: return (String[]) (actionStorageFile.list());
2293:
2294: case COPY_STORAGE_FILE_TO_REGULAR_ACTION:
2295: // SECURITY PERMISSION - MP1, OP4
2296: return ReuseFactory.getBoolean(FileUtil.copyFile(
2297: (WritableStorageFactory) storageFactory,
2298: actionStorageFile, actionRegularFile));
2299:
2300: case COPY_STORAGE_FILE_TO_STORAGE_ACTION:
2301: // SECURITY PERMISSION - MP1, OP4
2302: return ReuseFactory.getBoolean(FileUtil.copyFile(
2303: (WritableStorageFactory) storageFactory,
2304: actionStorageFile, actionToStorageFile));
2305:
2306: case REGULAR_FILE_GET_CANONICALPATH_ACTION:
2307: // SECURITY PERMISSION - MP1
2308: return (String) (actionRegularFile.getCanonicalPath());
2309:
2310: case STORAGE_FILE_GET_CANONICALPATH_ACTION:
2311: // SECURITY PERMISSION - MP1
2312: return (String) (actionStorageFile.getCanonicalPath());
2313: }
2314: return null;
2315: } // end of run
2316: }
|